zettelkasten/OneNoteExport/Technik/SQL/02_New-QSCSQLInstanceADObjects.md
Ralf Koop 5a108aa2b4 .
2023-08-25 23:29:11 +02:00

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