BW.Utils.GroupPolicy.psm1
<# .SYNOPSIS Increments the GPO version number causing the policy to refresh. .DESCRIPTION Increments the GPO version number causing the policy to refresh. You can increment either User, Computer, or Both version numbers. Handles updating AD and the GPT.INI file. If the file or AD update fails the function should revert the version number. The function should take pipeline input from Get-GPO. .PARAMETER Guid The GPO guid to increment .PARAMETER Name The GPO name to increment .PARAMETER Increment Choose to increment User, Computer, or Both .PARAMETER Domain See Get-GPO .PARAMETER Server See Get-GPO .EXAMPLE Get-GPO -Name 'Default Domain Policy' | Invoke-GPOIncrementVersion -Increment Computer Increments the computer policy version for the Default Domain Policy # function Invoke-GPOIncrementVersion { [CmdletBinding( DefaultParameterSetName = 'Guid', SupportsShouldProcess, ConfirmImpact='Low' )] param( [Parameter( ParameterSetName = 'Guid', Mandatory, ValueFromPipelineByPropertyName, Position = 1 )] [Alias( 'Id' )] [guid[]] $Guid, [Parameter( ParameterSetName = 'Name', Mandatory )] [string[]] $Name, [Parameter( Mandatory )] [ValidateSet( 'Both', 'Computer', 'User' )] [string] $Increment, [string] $Domain, [string] $Server ) process { $PSBoundParameters.Remove( $PSCmdlet.ParameterSetName ) > $null if ( $PSBoundParameters.ContainsKey( 'Increment' ) ) { $PSBoundParameters.Remove( 'Increment' ) > $null } $GPOs = switch ( $PSCmdlet.ParameterSetName ) { 'Guid' { $Guid | ForEach-Object { Get-GPO -Guid $_ @PSBoundParameters } } 'Name' { $Name | ForEach-Object { Get-GPO -Name $_ @PSBoundParameters } } } $GPOs | ForEach-Object { # find the GPO in AD $GetGpoObjectSplat = @{ SearchBase = "CN=Policies,CN=System,$( $_.DomainName.Split('.').ForEach({"DC=$_"}) -join ',' )" Filter = "Name -eq '$( $_.Id.ToString('B') )' -and objectClass -eq 'groupPolicyContainer'" Server = $_.DomainName Properties = 'DisplayName', 'versionNumber', 'gPCFileSysPath' } $GpoObject = Get-ADObject @GetGpoObjectSplat # first calculate the existing version numbers # User Policy version is the left 16 bits # Computer Policy version is the right 16 bits $UserPolicyVersion = $GpoObject.versionNumber -shr 16 $ComputerPolicyVersion = $GpoObject.versionNumber -band 0xffff # increment only the requested version if ( $Increment -eq 'Both' -or $Increment -eq 'User' ) { $UserPolicyVersion += 1 } if ( $Increment -eq 'Both' -or $Increment -eq 'Computer' ) { $ComputerPolicyVersion += 1 } # move the user policy back to the left, then add the computer policy $NewVersion = ( $UserPolicyVersion -shl 16 ) + $ComputerPolicyVersion # actually apply the change if ( $PSCmdlet.ShouldProcess( "$($GpoObject.DisplayName) $($GpoObject.Name)", "increment the version number for $($Increment.ToLower())" ) ) { $ConfirmPreference = 'None' $GptIniPath = Join-Path $GpoObject.gPCFileSysPath 'GPT.INI' $BkpIniPath = Join-Path $GpoObject.gPCFileSysPath 'GPT.INI-BAK' if ( -not( Test-Path -Path $GptIniPath -PathType Leaf ) ) { Write-Error "Could not find GPO $($GpoObject.DisplayName) $($GpoObject.Name) in file system!" return } try { # make a backup Copy-Item -Path $GptIniPath -Destination $BkpIniPath -Force -ErrorAction Stop # update the version $IniContent = Get-Content -Path $GptIniPath $IniContent = $IniContent -replace 'Version=\d+', "Version=$NewVersion" $IniContent | Set-Content -Path $GptIniPath -Encoding Ascii -ErrorAction Stop } catch { Remove-Item -Path $BkpIniPath -ErrorAction SilentlyContinue Write-Error "Could not modify the GPO version for $($GpoObject.DisplayName) $($GpoObject.Name) in file system!" return } try { $GpoObject | Set-ADObject -Replace @{ versionNumber = $NewVersion } -ErrorAction Stop } catch { Copy-Item -Path $BkpIniPath -Destination $GptIniPath -Force | Out-Null Write-Error "Could not modify the GPO version for $($GpoObject.DisplayName) $($GpoObject.Name) in AD!" return } finally { Remove-Item -Path $BkpIniPath -ErrorAction SilentlyContinue } } } } } #> <# .SYNOPSIS Get GPO links in a domain .DESCRIPTION Bastardizes Set-GPLink to return Microsoft.GroupPolicy.GPLink objects for GPLinks in a domain. .EXAMPLE Get-GPLink -Name 'Default Domain Policy' .EXAMPLE Get-GPLink -Guid 31b2f340-016d-11d2-945f-00c04fb984f9 #> function Get-GPLink { [CmdletBinding( PositionalBinding = $false )] param( [Parameter( ParameterSetName = 'Guid', Mandatory )] [guid[]] $Guid, [Parameter( ParameterSetName = 'Name', Mandatory )] [string[]] $Name, [string] $Target, [string] $Domain = $env:USERDNSDOMAIN, [string] $Server ) process { $Server = Get-ADDomainController -DomainName $Domain -Discover | Select-Object -ExpandProperty HostName -First 1 $RootDSE = Get-ADRootDSE -Server $Server $GPSplat = @{} if ( $Domain ) { $GPSplat.Domain = $Domain } if ( $Server ) { $GPSplat.Server = $Server } if ( $Name ) { $Guid = $Name.ForEach({ Get-GPO -Name $_ @GPSplat }).Id } foreach ( $CurrentGuid in $Guid ) { $SearchSplat = @{ Server = $Server Filter = "gplink -like '*$CurrentGuid*'" Properties = 'DistinguishedName' } [System.Collections.Generic.List[string]]$Result = @() # handle specific searches if ( $Target ) { $SearchSplat.SearchBase = $Target $SearchSplat.SearchScope = 'Base' Get-ADObject @SearchSplat | ForEach-Object { $Result.Add( $_.DistinguishedName ) } } # handle generic searches else { Get-ADObject -SearchBase $RootDSE.defaultNamingContext @SearchSplat | ForEach-Object { $Result.Add( $_.DistinguishedName ) } Get-ADObject -SearchBase $RootDSE.configurationNamingContext @SearchSplat | ForEach-Object { $Result.Add( $_.DistinguishedName ) } } # use Set-GPLink to get a GPLink object reference foreach ( $DistinguishedName in $Result ) { Set-GPLink -Guid $CurrentGuid -Target $DistinguishedName @GPSplat } } } } |