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: > > 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