Providers/AD/Private/Set-ADTestPolicyUserRight.ps1
|
function Set-ADTestPolicyUserRight { <# .SYNOPSIS Writes user rights assignments into a Group Policy object. .DESCRIPTION The GroupPolicy module can set registry-backed policy but has no cmdlet for User Rights Assignment, so this writes the [Privilege Rights] section of the GPO's security template directly and then does the three things the cmdlets would otherwise have done: 1. Writes GptTmpl.inf under Machine\Microsoft\Windows NT\SecEdit. 2. Registers the Security client-side extension in gPCMachineExtensionNames, or a client has no reason to read the file. 3. Bumps the computer half of the version, in both the directory and GPT.INI, or clients treat the policy as unchanged. Accounts are written as SIDs prefixed with an asterisk, which is the form secedit uses and the only form that survives a renamed or moved account. The SYSVOL location comes from the GPO's own gPCFileSysPath attribute rather than being assembled from the domain name, so it stays correct on a member server and wherever SYSVOL actually lives. .PARAMETER PolicyId GUID of the Group Policy object to write into. .PARAMETER Right The privilege constants to assign, for example SeDenyNetworkLogonRight. .PARAMETER Account Directory objects to name in each right. Anything exposing a SID works. .EXAMPLE Set-ADTestPolicyUserRight -PolicyId $gpo.Id -Right 'SeDenyNetworkLogonRight' -Account $users .OUTPUTS None. .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [CmdletBinding(SupportsShouldProcess = $true)] [OutputType([void])] param( [Parameter(Mandatory = $true)] [guid]$PolicyId, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]]$Right, [Parameter(Mandatory = $true)] [object[]]$Account ) $sids = @($Account | ForEach-Object { $_.SID.Value } | Where-Object { $_ } | Sort-Object -Unique) if ($sids.Count -eq 0) { throw 'No usable SIDs in the supplied accounts' } $adPath = "CN={$PolicyId},CN=Policies,CN=System,$((Get-ADDomain).DistinguishedName)" $props = 'gPCFileSysPath', 'gPCMachineExtensionNames', 'versionNumber' $gpoObject = Get-ADObject -Identity $adPath -Properties $props -ErrorAction Stop $secEdit = Join-Path $gpoObject.gPCFileSysPath 'Machine\Microsoft\Windows NT\SecEdit' $template = Join-Path $secEdit 'GptTmpl.inf' if (-not $PSCmdlet.ShouldProcess($template, 'Write user rights')) { return } if (-not (Test-Path -LiteralPath $secEdit)) { $null = New-Item -Path $secEdit -ItemType Directory -Force } $accountList = ($sids | ForEach-Object { "*$_" }) -join ',' $lines = [System.Collections.Generic.List[string]]::new() $lines.Add('[Unicode]') $lines.Add('Unicode=yes') $lines.Add('[Version]') $lines.Add('signature="$CHICAGO$"') $lines.Add('Revision=1') $lines.Add('[Privilege Rights]') foreach ($privilege in $Right) { $lines.Add("$privilege = $accountList") } # secedit reads this as Unicode; writing it as anything else produces a template the # client silently ignores. [System.IO.File]::WriteAllLines($template, $lines, [System.Text.UnicodeEncoding]::new($false, $true)) # Register the Security CSE so a client knows to read what was just written. The pair # is {CSE}{snap-in}; appending would duplicate it on a second run. $securityCse = '[{827D319E-6EAC-11D2-A4EA-00C04F79F83A}{803E14A0-B4FB-11D0-A0D0-00A0C90F574B}]' $currentCse = [string]$gpoObject.gPCMachineExtensionNames if ($currentCse -notlike "*827D319E-6EAC-11D2-A4EA-00C04F79F83A*") { Set-ADObject -Identity $adPath -Replace @{ gPCMachineExtensionNames = ($currentCse + $securityCse) } -ErrorAction Stop } # Computer settings live in the low word of versionNumber. Incrementing by one is what # the console does for a computer-side edit. $newVersion = [int]$gpoObject.versionNumber + 1 Set-ADObject -Identity $adPath -Replace @{ versionNumber = $newVersion } -ErrorAction Stop $gptIni = Join-Path $gpoObject.gPCFileSysPath 'GPT.INI' if (Test-Path -LiteralPath $gptIni) { $ini = Get-Content -LiteralPath $gptIni $ini = $ini | ForEach-Object { if ($_ -match '^\s*Version\s*=') { "Version=$newVersion" } else { $_ } } Set-Content -LiteralPath $gptIni -Value $ini -Encoding ASCII } else { Set-Content -LiteralPath $gptIni -Value @('[General]', "Version=$newVersion") -Encoding ASCII } Write-Verbose "Wrote $($Right.Count) right(s) naming $($sids.Count) account(s) to $template" } |