18 KiB
New-QSCSQLInstanceADObjects
Dienstag, 7. Februar 2017
13:45
User und Gruppen für die SQL Installation anlegen im AD
<#
.SYNOPSIS
Create a SQL service user including all necessary AD groups and SPN rights
.DESCRIPTION
Creates a SQL service user with a cryptic password.
Also it creates the "SQL_All Servers_SYSADMIN" and "SQL_All Servers_PUBLIC" AD group if not existing.
The service user will gain READ/WRITE access for the service principal name attribute.
The user will be added to all security Deny Zones, except "Log on as a service"
According to Technet the following right groups will be added, if present
* Adjust memory quotas for a process (SeIncreaseQuotaPrivilege)
* Bypass traverse checking (SeChangeNotifyPrivilege)
* Create global objects (SeCreateGlobalPrivilege)
* Impersonate a client after authentication (SeImpersonatePrivilege)
* Lock pages in memory (SeLockMemoryPrivilege)
* Log on as a service (SeServiceLogonRight)
* Perform volume maintenance tasks (SeManageVolumePrivilege)
* Replace a process-level token (SeAssignPrimaryTokenPrivilege)
Reference: https://technet.microsoft.com/en-us/library/ms143504(v=sql.120).aspx
Created by Fabian Bader @ QSC AG 2016
.PARAMETER InstanceName
Specifies the name of the SQL instance to connect to without hostname
.PARAMETER Zone
Specifies the security Zone.
Valid zones are: ADM, DOM, RED, BLUE
.PARAMETER Domain
Specifies the name of the domain (optional)
Default is the current users domain.
.PARAMETER UseGroupManagedServiceAccount
If present a groupManagedServiceAccount will be created instead of a normal service account
Fully support with SQL 2016 and above
.PARAMETER Computername
Specifies the name of the computer on which SQL is installed (optional).
The ServerRightsOU will be autodiscovered if set.
Default is null.
.PARAMETER ServerRightsOU
Specifies the DN of the SQL server OU without the domain (DC=EXAMPLE,DC=COM) (optional)
e.g. "OU=SQL,OU=SYS,OU=RIGHTS,OU=GROUPS,OU=CENTRAL"
.PARAMETER SQLRightsOU
Specifies the DN of the SQL rights OU without the domain (DC=EXAMPLE,DC=COM) (optional)
Default is: "OU=SQL,OU=RIGHTS,OU=GROUPS,OU=CENTRAL"
.PARAMETER ManagementDomain
Specifies the name of the management domain (optional)
Only applies to the Pure Enterprise Cloud
Default is 'service.pure-enterprise-cloud.de'
.PARAMETER SharePointGroups
If specified the rights groups for a sharepoint instance will be created
.EXAMPLE
.\New-QSCSQLInstanceADObjects.ps1 -InstanceName QSCDBP01J
#>
Param (
Parameter(Mandatory=\$true)
ValidateScript({
if (\$\_ -match \"\[J-Zj-z\]\$\")
{
return \$true
}
throw \"Instance name is invalid \'\$\_\'\`n\`tDoes not end with one of the following chacters J,K,L,M,N,O,P,Q,R,T,U,V,W,X,Y,Z\"
})\]
\$InstanceName,
\[Parameter(Mandatory=\$true)\]
\[ValidateSet(\"ADM\",\"DOM\",\"RED\",\"BLUE\")\]
\$Zone,
\$Domain=\$env:USERDNSDOMAIN,
\[switch\]\$UseGroupManagedServiceAccount,
\[ValidateScript({
if (\$\_ -match \"\^\[\\w-\]+\$\")
{
return \$true
}
throw \"Instance name is invalid\"
})\]
\$Computername,
\[ValidateScript({
if (\$\_ -notmatch \"DC=\" -and \$\_ -match \"\^OU=\")
{
return \$true
}
throw \"OU dn is invalid \'\$\_\'\"
})\]
\$ServerRightsOU=\"OU=SQL,OU=SYS,OU=RIGHTS,OU=GROUPS,OU=CENTRAL\",
\[ValidateScript({
if (\$\_ -notmatch \"DC=\" -and \$\_ -match \"\^OU=\")
{
return \$true
}
throw \"OU dn is invalid \'\$\_\'\"
})\]
\$SQLRightsOU=\"OU=SQL,OU=RIGHTS,OU=GROUPS,OU=CENTRAL\",
\[switch\] \$SharePointGroups,
\$ManagementDomain=\"service.pure-enterprise-cloud.de\"
)
\#region Import modules
If ( ! (Get-module ActiveDirectory ) ) {
Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue
}
\#endregion
\#Check domains
\#Get AD information
try {
if (!(\$ActiveDomainController)) {
> \$ActiveDomainController = Get-ADDomainController -DomainName \$Domain -Discover -ForceDiscover -Writable
>
> \$ActiveDomainController = \$ActiveDomainController.HostName\[0\]
Write-Output \"\[AD_CONTROLLER\]\`t\`tUsing discovered ADDC: \$(\$ActiveDomainController)\"
} else {
Write-Output \"\[AD_CONTROLLER\]\`t\`tUsing static ADDC: \$(\$ActiveDomainController)\"
}
\$ActiveDirectory = Get-ADDomain \$Domain -Server \$ActiveDomainController
} catch {
Write-Error \"\$Domain is not reachable\"
Exit
}
\$TrustedDomains = Get-ADObject -Server \$Domain -Filter {ObjectClass -eq \"trustedDomain\"} -Properties \*
if (\$TrustedDomains.trustPartner -like \$ManagementDomain) {
Write-Output \"\[AD_TRUST\]\`t\`t\`tFound management domain \$ManagementDomain in trusted domains\"
\$ManagementActiveDirectory = Get-ADDomainController -DomainName \$ManagementDomain -Discover -ForceDiscover -Writable
> \$ManagementActiveDirectory = \$ManagementActiveDirectory.HostName\[0\]
\$AddToMgmt = \$True
} else {
Write-Verbose \"\[AD_TRUST\]\`t\`t\`tManagement domain \$ManagementDomain not found in trusted domains\"
\$AddToMgmt = \$False
}
\#
\#Validate Sercurity Zone
try {
> Get-ADGroup -ErrorAction SilentlyContinue -Identity \"\$Zone SECURITY ZONE\" -Server \$ActiveDomainController \| Out-Null
} catch {
Write-Host -ForegroundColor Red \"The Security Zone \$Zone does not exist\" -ErrorAction SilentlyContinue
Exit
}
\#region Autodiscover computer OU
try {
if (\$Computername) {
> \$ComputerDN = Get-ADComputer -Filter { Name -eq \$Computername} -Server \$ActiveDomainController
>
> \$ComputerDN = \$ComputerDN.DistinguishedName -replace \"CN=\[\\w-\]+,\", \'\'
\$ComputerDN = \$ComputerDN -replace \"OU=SERVERS\",\"OU=SYS,OU=RIGHTS,OU=GROUPS\"
\$ServerRightsOU = \$ComputerDN -replace \",DC=.\*\$\", \'\'
Write-Output \"\[COMPUTER_OU\]\`t\`tUsing discovered OU: \$(\$ServerRightsOU)\"
} else {
Write-Output \"\[COMPUTER_OU\]\`t\`tUsing static OU: \$(\$ServerRightsOU)\"
}
} catch {
Write-Error \"\$Computername not found in \$Domain\"
Exit
}
\#endregion
\#region Test OUs
try {
\$ServerRightsOUDN = \"\$(\$ServerRightsOU),\$(\$ActiveDirectory.DistinguishedName)\"
Get-ADObject \$ServerRightsOUDN -Server \$ActiveDomainController \| Out-Null
Write-Output \"\[VALID_OU\]\`t\`tServer rights OU:\`t\$ServerRightsOUDN\"
} catch {
Write-Error \"Could not find \$ServerRightsOUDN\"
Exit
}
try {
\$SQLRightsOUDN = \"\$(\$SQLRightsOU),\$(\$ActiveDirectory.DistinguishedName)\"
Get-ADObject \$SQLRightsOUDN -Server \$ActiveDomainController \| Out-Null
Write-Output \"\[VALID_OU\]\`t\`tSQL rights OU:\`t\`t\$SQLRightsOUDN\"
} catch {
Write-Error \"Could not find \$SQLRightsOUDN\"
Exit
}
if (\$UseGroupManagedServiceAccount.IsPresent) {
\$gMSARightsOU=\"OU=GMSA,OU=RIGHTS,OU=GROUPS,OU=CENTRAL,\$(\$ActiveDirectory.DistinguishedName)\"
try {
Get-ADObject \$gMSARightsOU -Server \$ActiveDomainController \| Out-Null
Write-Output \"\[VALID_OU\]\`t\`tGMSA rights OU:\`t\`t\$gMSARightsOU\"
} catch {
Write-Warning \"Could not find \$gMSARightsOU\"
Exit
}
\$gMSAOU=\"CN=Managed Service Accounts,\$(\$ActiveDirectory.DistinguishedName)\"
try {
Get-ADObject \$gMSAOU -Server \$ActiveDomainController \| Out-Null
Write-Output \"\[VALID_OU\]\`t\`tGMSA OU:\`t\`t\$gMSAOU\"
} catch {
Write-Warning \"Could not find \$gMSAOU\"
Exit
}
}
\#endregion
\#region Create global groups
\$GlobalGroups = @(\"SQL_All Servers_SYSADMIN\",\"SQL_All Servers_PUBLIC\")
foreach (\$GroupName in \$GlobalGroups) {
\$SecurityGroup = Get-ADGroup -Filter { Name -eq \$GroupName } -Server \$ActiveDomainController
if ( (\$SecurityGroup \| Measure-Object).Count -ne 0 ) {
Write-Output \"\[GLOBAL_GROUP\]\`t\`t\`\'\$GroupName\`\' already exists\"
\$GroupNesting=\$false
} else {
try {
New-ADGroup -Name \$GroupName -GroupCategory Security -GroupScope DomainLocal -Path \$SQLRightsOUDN -Description \"Global SQL rights group\" -Server \$ActiveDomainController -EA Stop
Write-Output \"\[GLOBAL_GROUP\]\`t\`t\`\'\$GroupName\`\' created\"
\$GroupNesting=\$true
} catch {
Write-Warning \"\[GLOBAL_GROUP\]\`t\`tFailed to create \`\'\$GroupName\`\'.\`tError: \$(\$(\$\_.Exception).Message)\"
Exit
}
}
\#region Nest the global groups from the management domain
if (\$AddToMgmt) {
try {
\# Add management group to local group to allow join computer to domain
\$ManagementDomainGroup = \$GroupName -replace \"SQL\_\", \"SQL_CUSTOMER\_\"
\$ManagementDomainGroup = Get-ADGroup \$ManagementDomainGroup -Server \$ManagementActiveDirectory
Add-ADGroupMember \$GroupName -Members \$ManagementDomainGroup -Server \$ActiveDomainController
Write-Output \"\[AddADGroupMember\]\`tAdded \`\"\$ManagementDomainGroup\`\" to \`\"\$GroupName\`\"\"
} catch {
Write-Warning \"\[AddADGroupMember\]\`tCould not add \`\"\$ManagementDomainGroup\`\" Failed for \`\"\$GroupName\`\"\`n\`tError: \$(\$(\$\_.Exception).Message)\"
}
}
\#endregion
}
\#endregion
\#region Create the necessary AD groups
if (\$SharePointGroups.IsPresent) {
\$SQLSuffixes=\"DBCREATOR\",\"SECURITYADMIN\",\"SYSADMIN\",\"MSDB-SQLAgentOperator\", \"MODEL-db_datareader\", \"MASTER-Brent_Tools_Execute\", \"MODEL-db_owner\", \"View-Server-State\", \"PROCESSADMIN\", \"VIEW-ANY-DATABASE\", \"MODEL-db_backupoperator\", \"ALTER-ANY-AVAILABILITY-GROUP\",\"MSDB-sp_delete_database_backuphistory_Exec\",\"MASTER-DBAExecuteRole_sp_changedbowner\"
} else {
\$SQLSuffixes=\"DBCREATOR\",\"DBO\",\"EDIT\",\"READ\",\"SECURITYADMIN\",\"SERVERADMIN\",\"SYSADMIN\"
}
foreach (\$SQLSuffix in \$SQLSuffixes) {
try {
New-ADGroup -Path \$SQLRightsOUDN -Name \"SQL\_\$(\$InstanceName)\_\$(\$SQLSuffix)\" -Description \"SQL Instance: \$InstanceName\" -GroupScope DomainLocal -GroupCategory Security -Server \$ActiveDomainController
Write-Output \"\[NEWADGROUP\]\`t\`tCreated AD Group SQL\_\$(\$InstanceName)\_\$(\$SQLSuffix)\"
} catch {
Write-Warning \"\[NEWADGROUP\]\`t\`tCould not create AD Group SQL\_\$(\$InstanceName)\_\$(\$SQLSuffix)\"
}
}
\#endregion
\#region Variables for user GMSA group
if (\$UseGroupManagedServiceAccount.IsPresent) {
\$Username = \"svc\_\$(\$InstanceName)\"
\# Maximum of 15 characters
\$Username = \$Username.Substring(0,\[System.Math\]::Min(15, \$Username.Length))
} else {
\$Username = \"svc\_\$(\$InstanceName)\_SQL\"
\$DisplayName = \"\$(\$InstanceName)\_SQL Service\"
\# Maximum of 20 characters
\$Username = \$Username.Substring(0,\[System.Math\]::Min(20, \$Username.Length))
}
\# Rights Group
\$GroupName = \"GMSA\_\$(\$Username)\"
\$Description = \"Serviceuser for SQL instance: \$InstanceName\"
\#endregion
\#region GMSA rights group and members
if (\$UseGroupManagedServiceAccount.IsPresent) {
try {
\$SecurityGroup = Get-ADGroup -Filter { Name -eq \$GroupName } -Server \$ActiveDomainController
if ( (\$SecurityGroup \| Measure-Object).Count -ne 0 ) {
Write-Output \"\[NEWADGROUP\]\`t\`t\$GroupName already exists\"
} else {
New-ADGroup -Name \$GroupName -GroupCategory Security -GroupScope DomainLocal -Path \$gMSARightsOU -Description \"Permission to use group managed service account \$UserName\" -Server \$ActiveDomainController
\$SecurityGroup = Get-ADGroup -Filter { Name -like \$GroupName } -Server \$ActiveDomainController
Write-Output \"\[NEWADGROUP\]\`t\`tCreated AD Group \$(\$GroupName)\"
}
} catch {
Write-Error \"\[NEWGROUP\]\`t\`tCould not create \$(\$GroupName). Error: \$(\$(\$\_.Exception).Message)\"
Exit
}
\# Add computers objects to GMSA Rights group
\# Build searchfilter
\$Filter = \$InstanceName -replace \"\\w\$\",\'\*\'
Write-Verbose \"\[AddADGroupMember\]\`tSearchfilter: \$Filter\"
\$Computers = Get-ADComputer -Filter { Name -like \$Filter} -Server \$ActiveDomainController \| Where-Object { \$\_.Name -match \"\^\\w+\\d+\[\\d\\w\]\$\"}
if ( \$Computers ) {
if (\$Computers -is \[system.array\]) {
Write-Verbose \"\[AddADGroupMember\]\`tComputers found: \$(\$Computers.Count)\"
foreach (\$Computer in \$Computers) {
try {
Add-ADGroupMember \$GroupName -Members \$Computer.DistinguishedName -Server \$ActiveDomainController
Write-Output \"\[AddADGroupMember\]\`tAdded \`\"\$(\$Computer.DNSHostName)\`\" to \`\"\$GroupName\`\"\"
} catch {
Write-Warning \"\[AddADGroupMember\]\`tCould not add \`\"\$(\$Computer.DNSHostName)\`\" Failed for \`\"\$GroupName\`\"\`n\`tError: \$(\$(\$\_.Exception).Message)\"
}
}
} else {
Write-Verbose \"\[AddADGroupMember\]\`tComputers found: 1\"
try {
Add-ADGroupMember \$GroupName -Members \$Computers.DistinguishedName -Server \$ActiveDomainController
Write-Output \"\[AddADGroupMember\]\`tAdded \`\"\$(\$Computers.DNSHostName)\`\" to \`\"\$GroupName\`\"\"
} catch {
Write-Warning \"\[AddADGroupMember\]\`tCould not add \`\"\$(\$Computers.DNSHostName)\`\" Failed for \`\"\$GroupName\`\"\`n\`tError: \$(\$(\$\_.Exception).Message)\"
}
}
} else {
Write-Warning \"\[AddADGroupMember\]\`tFound no computers to add to \`\"\$GroupName\`\"\"
}
}
\#endregion
\#region Create Serviceuser
try {
if (\$UseGroupManagedServiceAccount.IsPresent) {
New-ADServiceAccount -name \$Username -DNSHostName \"\$(\$ActiveDirectory.DNSRoot)\" -PrincipalsAllowedToRetrieveManagedPassword \$GroupName -Server \$ActiveDomainController
Write-Output \"\[NEWADUSER\]\`t\`tGroupManagedServiceAccount created:\`t\`\"\$Username\`\"\"
} else {
\$UserPath = \"OU=SVC,OU=ACCOUNTS,OU=CENTRAL,\$(\$ActiveDirectory.DistinguishedName)\"
\# Add assembly to generate random password
Add-Type -AssemblyName System.Web -ErrorAction SilentlyContinue
\# Generate random password with 25 characters and 3 non alphanumeric characters
\$Password = \[System.Web.Security.Membership\]::GeneratePassword(25,3)
New-ADUser -Name \$Username -UserPrincipalName \"\$(\$Username)@\$(\$ActiveDirectory.DNSRoot)\" -SamAccountName \$Username -DisplayName \$DisplayName -AccountPassword (\$Password \| ConvertTo-SecureString -AsPlainText -Force) -Description \$Description -Enabled:\$true -Path \$UserPath -PasswordNeverExpires \$true -Server \$ActiveDomainController
Write-Output \"\[NEWADUSER\]\`t\`tUser created:\`t\`\"\$Username\`\"\"
Write-Output \"\[NEWADUSER\]\`t\`tGenerated password:\`t\$Password\"
}
} catch {
Write-Error \"\[NEWADUSER\]\`t\`tFailed. Error: \$(\$(\$\_.Exception).Message)\"
Exit
}
\#endregion
\#region
try {
\$UserObject = Get-ADObject -Filter { name -eq \$UserName } -Server \$ActiveDomainController
\$UserObject \| Set-ADObject -Replace \@{\"msDS-SupportedEncryptionTypes\" = \'24\'} -Server \$ActiveDomainController
Write-Output \"\[MODUSER\]\`t\`tDisabled RC4 for \$Username\"
} catch {
Write-Error \"\[MODUSER\]\`t\`tFailed. Error: \$(\$(\$\_.Exception).Message)\"
}
\#endregion
\#region Add serviceuser to right groups
\$PrivilegeRights = @(
\"Adjust memory quotas for a Process\",
\"Bypass traverse checking\",
\"Create global objects\",
\"Impersonate a client after authentication\",
\"Lock pages in memory\"
\"Log on as a service\",
\"Perform volume maintenance tasks\",
\"Replace a process level token\"
)
\$ADRightsGroups = Get-ADObject -SearchBase \$ServerRightsOUDN -SearchScope OneLevel -Filter { ObjectClass -eq \"Group\" } -Server \$ActiveDomainController
foreach (\$ADRightsGroup in \$ADRightsGroups) {
if (\$PrivilegeRights -match (\$ADRightsGroup.Name -replace \"SYS\_.\[\^ \]\* \",\'\') ) {
try {
Add-ADGroupMember \$ADRightsGroup.Name -Members \$UserObject -Server \$ActiveDomainController
Write-Output \"\[GROUPMEMBER\]\`t\`tAdded user \`\'\$Username\`\' to \`\'\$(\$ADRightsGroup.Name)\`\'.\"
} catch {
Write-Warning \"\[GROUPMEMBER\]\`t\`tFailed to add user to \`\'\$(\$ADRightsGroup.Name)\`\'. Error: \$(\$(\$\_.Exception).Message)\"
}
}
}
\#endregion
\#region Add serviceuser to Zone group and Deny groups
try {
\$SearchFilter = \"SYS\_\$(\$Zone) Deny\*\"
\$DenyGroups = Get-ADGroup -Filter {Name -like \$SearchFilter}
\$SearchFilter = \"SYS\_\$(\$Zone) Deny log on as a service\"
\$DenyGroups = \$DenyGroups \| Where-Object { \$\_.Name -notlike \$SearchFilter}
\$SecurityZoneGroup = Get-ADGroup -ErrorAction SilentlyContinue -Identity \"\$Zone SECURITY ZONE\" -Server \$ActiveDomainController
} catch {
Write-Warning \"\[DENY_GROUPS\]\`t\`t\`tCould not find Deny groups for \$(\$Zone) Security Zone\"
}
foreach (\$DenyGroup in \$DenyGroups) {
try {
Add-ADGroupMember \$DenyGroup.Name -Members \$UserObject -Server \$ActiveDomainController
Write-Output \"\[GROUPMEMBER\]\`t\`tAdded user \`\'\$Username\`\' to \`\'\$(\$DenyGroup.Name)\`\'.\"
} catch {
Write-Warning \"\[GROUPMEMBER\]\`t\`tFailed to add user to \`\'\$(\$DenyGroup.Name)\`\'. Error: \$(\$(\$\_.Exception).Message)\"
}
}
try {
Add-ADGroupMember \$SecurityZoneGroup.Name -Members \$UserObject -Server \$ActiveDomainController
Write-Output \"\[GROUPMEMBER\]\`t\`tAdded user \`\'\$Username\`\' to \`\'\$(\$SecurityZoneGroup.Name)\`\'.\"
} catch {
Write-Warning \"\[GROUPMEMBER\]\`t\`tFailed to add user to \`\'\$(\$SecurityZoneGroup.Name)\`\'. Error: \$(\$(\$\_.Exception).Message)\"
}
\#endregion
\#region Set read/write for attribute serviceprincipalname to the service account
try {
\#NT AUTHORITY\\SELF
\$IdentityReference = New-Object System.Security.Principal.SecurityIdentifier (\'S-1-5-10\')
\#ValidatedSPN
\$ObjectType = New-Object -TypeName GUID -ArgumentList \'f3a64788-5306-11d1-a9c5-0000f80367c1\'
\$InheritedObjectType = New-Object -TypeName GUID -ArgumentList \'00000000-0000-0000-0000-000000000000\'
\$AccessControlType = \"Allow\"
\$Inherit = \'None\'
\$ActiveDirectoryRights = \'ReadProperty, WriteProperty\'
\$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(\$IdentityReference,\$ActiveDirectoryRights,\$AccessControlType,\$ObjectType,\$Inherit,\$InheritedObjectType)
\$DN = \$UserObject.DistinguishedName
\$ADSI = \[ADSI\]\"LDAP://\$ActiveDomainController/\$DN\"
\$ADSI.psbase.ObjectSecurity.AddAccessRule(\$ACE)
\$ADSI.psbase.commitchanges()
Write-Output \"\[SETSPNRIGHT\]\`t\`tREAD/WRITE access for attribute serviceprincipalname set successfully for SELF on \`\'\$Username\`\'\"
} catch {
Write-Warning \"\[GROUPMEMBER\]\`t\`tFailed to set rights for serviceprincipalname on \`\'\$Username\`\'. Error: \$(\$(\$\_.Exception).Message)\"
}
\#endregion