BW.Utils.GroupPolicy.WMIFilter.psm1

using namespace System.DirectoryServices.ActiveDirectory
using namespace System.DirectoryServices
using namespace System.Security.Principal
using namespace System.Collections
using module .\classes\WmiFilterObject.psm1
using module .\classes\WmiFilterList.psm1

# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function ConvertFrom-WmiFilterString {

    [OutputType([WmiFilterList])]
    param(

        [Parameter(
            Mandatory,
            Position = 0
        )]
        [WmiFilterList]
        $InputObject

    )

    return , $InputObject

}

# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function ConvertTo-WmiFilterString {

    [OutputType([WmiFilterList])]
    param(

        [Parameter(
            Mandatory,
            Position = 0
        )]
        [WmiFilterList]
        $InputObject

    )

    return $InputObject.ToString()

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function New-WmiFilterObject {

    [OutputType([WmiFilterObject])]
    param (

        [Parameter(Mandatory, Position=1)]
        [ValidatePattern('^SELECT.*FROM.*WHERE.*$')]
        [ValidateNotNullOrEmpty()]
        [string]
        $Filter,

        [ValidateNotNullOrEmpty()]
        [string]
        $NameSpace = 'root\CIMv2',

        [ValidateSet('WQL')]
        [string]
        $Language = 'WQL',

        [WmiFilterList]
        $WmiFilterList

    )

    $WmiFilterObject = [WmiFilterObject]@{
            Filter      = $Filter
            NameSpace   = $NameSpace
            Language    = $Language
    }

    if ( $WmiFilterList -is [WmiFilterList] ) {

        $WmiFilterList.Add( $WmiFilterObject ) > $null

    } else {
        
        return $WmiFilterObject

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function New-WmiFilterList {

    [CmdletBinding(DefaultParameterSetName='EmptyList')]
    [OutputType([WmiFilterList])]
    param (

        [Parameter(ParameterSetName='FromObject', Mandatory, Position=1)]
        [WmiFilterObject[]]
        $WmiFilterObject,
        
        [Parameter(ParameterSetName='FromString', Mandatory, Position=1)]
        [ValidatePattern('^SELECT.*FROM.*WHERE.*$')]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Filter,

        [Parameter(ParameterSetName='FromString')]
        [ValidateNotNullOrEmpty()]
        [string]
        $NameSpace = 'root\CIMv2',

        [Parameter(ParameterSetName='FromString')]
        [ValidateSet('WQL')]
        [string]
        $Language = 'WQL'

    )

    $WmiFilterList = [WmiFilterList]::new()

    switch ( $PSCmdlet.ParameterSetName ) {

        'FromObject' {

            $WmiFilterList.AddRange( $WmiFilterObject ) > $null

        }

        'FromString' {

            $Filter | ForEach-Object {

                $WmiFilterList.Add([WmiFilterObject]@{
                    Filter      = $Filter
                    NameSpace   = $NameSpace
                    Language    = $Language
                }) > $null
    
            }

        }

    }

    # note that the comma is important so PowerShell doesn't convert to an Array
    return , $WmiFilterList

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Get-GPWmiFilter {

    [CmdletBinding()]
    param(
    
        [Parameter(
            ParameterSetName = 'GetAll',
            Mandatory
        )]
        [switch]
        $All,
        
        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            DontShow,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID', 
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'Id' )]
        [guid]
        $Guid,

        [Parameter(
            ParameterSetName = 'ByGPO',
            Mandatory,
            Position = 0,
            ValueFromPipeline
        )]
        [Microsoft.GroupPolicy.Gpo]
        $GPO,

        [Parameter(
            ParameterSetName = 'GetAll',
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByName',
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Parameter(
            ParameterSetName = 'GetAll'
        )]
        [Parameter(
            ParameterSetName = 'ByName'
        )]
        [Parameter(
            ParameterSetName = 'ByGUID'
        )]
        [Alias( 'DC' )]
        [string]
        $Server,

        [Parameter(
            DontShow
        )]
        [switch]
        $AsDirectoryEntry

    )

    process {

        # if we are passed a GPO on the pipeline we do a special lookup
        if ( $GPO ) {

            if ( $GPO.WmiFilter ) {

                $GPO.WmiFilter.Path -replace '^MSFT_SomFilter\.' -split '(?<="),' | ForEach-Object {

                    $Key, $Value = $_.Split('=').ForEach({ $_.Trim('"') })
                    
                    if ( $Key -eq 'ID' ) { $Guid = $Value }
                    if ( $Key -eq 'Domain' ) { $Domain = $Value }

                }

                return Get-GPWmiFilter -Guid $Guid -Domain $Domain -AsDirectoryEntry:$AsDirectoryEntry.IsPresent


            } else {

                Write-Warning "No WMI filter attached to GPO"
                return
            }

        }

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        $NamingContext = ([adsi]"LDAP://$Domain/RootDSE").defaultNamingContext
        $SOMContainer = [adsi]"LDAP://$Server/CN=SOM,CN=WMIPolicy,CN=System,$NamingContext"
        $SearchFilter = switch ( $PSCmdlet.ParameterSetName ) {
            'GetAll' { '(objectclass=msWMI-Som)' }
            'ByName' { "(&(objectclass=msWMI-Som)(mswmi-name=$Name))" }
            'ByGUID' { "(&(objectclass=msWMI-Som)(mswmi-id=$($Guid.ToString('B'))))" }
        }
        
        $Searcher = [adsisearcher]::new( $SOMContainer, $SearchFilter )
        $Searcher.PropertiesToLoad.Add( 'adspath' ) > $null
        
        [object[]]$DirectoryEntries = $Searcher.FindAll().ForEach({ [adsi]$_.Path })

        $Searcher.Dispose() > $null
        $SOMContainer.Dispose() > $null

        if ( $DirectoryEntries.Count -eq 0 ) {

            if ( -not $All ) {

                Write-Error 'No WMI filter found!'

            }

            return

        }
        
        if ( $AsDirectoryEntry ) {

            return $DirectoryEntries

        }
        
        $DirectoryEntries | Select-Object `
            @{ N='DisplayName'  ; E={ $_.'mswmi-name' }},
            @{ N='DomainName'   ; E={ $Domain }},
            @{ N='Server'       ; E={ $Server }},
            @{ N='Owner'        ; E={ $_.PsBase.ObjectSecurity.Owner }},
            @{ N='Id'           ; E={ [guid][string]$_.'mswmi-id' }},
            @{ N='Description'  ; E={ $_.'mswmi-parm1' }},
            @{ N='Author'       ; E={ $_.'mswmi-author' }},
            @{ N='Filters'      ; E={ , [WmiFilterList][string]$_.'mswmi-parm2' }}

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function New-GPWmiFilter {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'Low'
    )]
    param(

        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Alias( 'Id' )]
        [guid]
        $Guid = ( [guid]::NewGuid() ),

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [string]
        $Description,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [string]
        $Author = ([WindowsIdentity]::GetCurrent().Name),

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [WmiFilterList]
        $Filters,

        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Alias( 'DC' )]
        [string]
        $Server
        
    )

    process {

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        $TestParams = @{
            Domain = $Domain
            Server = $Server
        }

        # Check for existing filter with same name
        if ( $ExistingFilter = Get-GPWmiFilter -Name $Name @TestParams -ErrorAction SilentlyContinue ) {
            
            Write-Error ( 'WMI filter ''{0}'' already exists in domain {1}!' -f $ExistingFilter.DisplayName, $Domain )
            return

        }
        
        # check for existing filter with same quid
        if ( $ExistingFilter = Get-GPWmiFilter -Guid $Guid @TestParams -ErrorAction SilentlyContinue ) {

            Write-Error ( 'WMI filter with GUID ''{0}'' already exists in domain {1}!' -f $ExistingFilter.Id.ToString('B'), $Domain )
            return

        }

        $NamingContext = ([adsi]"LDAP://$Domain/RootDSE").defaultNamingContext
        $SOMContainer = [adsi]"LDAP://$Server/CN=SOM,CN=WMIPolicy,CN=System,$NamingContext"

        # create a time stamp
        $Created = [datetime]::UtcNow.ToString('yyyyMMddHHmmss.ffffff-000')

        try {

            if ( $PSCmdlet.ShouldProcess( $Name, 'create' ) ) {
            
                $WmiFilter = $SOMContainer.Create( 'msWMI-Som', "CN=$($Guid.ToString('B'))" )

                $WmiFilter.Put( 'msWMI-Name',               $Name                                       )
                $WmiFilter.Put( 'msWMI-Parm1',              $Description                                )
                $WmiFilter.Put( 'msWMI-Parm2',              $Filters.ToString()                         )
                $WmiFilter.Put( 'msWMI-Author',             $Author                                     )
                $WmiFilter.Put( 'msWMI-ID',                 $Guid.ToString('B')                         )
                $WmiFilter.Put( 'instanceType',             4                                           )
                $WmiFilter.Put( 'showInAdvancedViewOnly',   'TRUE'                                      )
                $WmiFilter.Put( 'distinguishedname',        "CN=$($Guid.ToString('B')),$SOMContainer"   )
                $WmiFilter.Put( 'msWMI-ChangeDate',         $Created                                    )
                $WmiFilter.Put( 'msWMI-CreationDate',       $Created                                    )

                $WmiFilter.SetInfo()

            }

        } catch {

            Write-Error ( 'Failed to create WMI filter ''{0}'' in domain {1}. Please verify that you have rights to create WMI filters. You may also need to first enable ''Allow System Only Change'' for the domain. You can use the Set-ADSystemOnlyChange cmdlet to make this change.' -f $Name, $Domain )
        
        } finally {

            $SOMContainer.Dispose()
        
        }

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Set-GPWmiFilter {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(

        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            DontShow,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID', 
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'Id' )]
        [guid]
        $Guid,

        [string]
        $NewName,

        [string]
        $Description,

        [WmiFilterList]
        $Filters,

        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Alias( 'DC' )]
        [string]
        $Server
        
    )

    process {

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        $GetParams = @{
            Domain = $Domain
            Server = $Server
        }
        if ( $Guid ) { $GetParams.Guid = $Guid }
        elseif ( $Name ) { $GetParams.Name = $Name }

        # verify that the WMI filter exists
        $ExistingFilter = Get-GPWmiFilter @GetParams -AsDirectoryEntry -ErrorAction Stop

        $IsUpdated = $false

        if ( $NewName ) {

            $ExistingFilter.'msWMI-Name' = $NewName
            $IsUpdated = $true

        }

        if ( $PSBoundParameters.ContainsKey( 'Description' ) -and [string]::IsNullOrEmpty( $Description ) ) {

            $ExistingFilter.PutEx( 1, 'msWMI-Parm1', 0 )
            $IsUpdated = $true

        } elseif ( $PSBoundParameters.ContainsKey( 'Description' ) ) {

            $ExistingFilter.'msWMI-Parm1' = $Description
            $IsUpdated = $true

        }

        if ( $Filters ) {

            $ExistingFilter.'msWMI-Parm2' = $Filters.ToString()
            $IsUpdated = $true

        }

        if ( $IsUpdated ) {

            if ( $PSCmdlet.ShouldProcess( $ExistingFilter.'msWMI-Name', 'update' ) ) {

                try {

                    $ExistingFilter.'msWMI-ChangeDate' = [datetime]::UtcNow.ToString('yyyyMMddHHmmss.ffffff-000')
                    $ExistingFilter.CommitChanges()

                } catch {

                    Write-Error ( 'Failed to update WMI filter ''{0}'' in domain {1}. Please verify that you have rights to create WMI filters. You may also need to first enable ''Allow System Only Change'' for the domain. You can use the Set-ADSystemOnlyChange cmdlet to make this change.' -f $ExistingFilter.'msWMI-Name'[0], $Domain )

                }

            }
        
        } else {

            Write-Warning ( 'No changes made to WMI filter ''{0}'' in domain {1}.' -f $ExistingFilter.'msWMI-Name'[0], $Domain )

        }

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Remove-GPWmiFilter {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(

        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            DontShow,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID', 
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'Id' )]
        [guid]
        $Guid,

        [string]
        $NewName,

        [string]
        $Description,

        [WmiFilterList]
        $Filters,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DC' )]
        [string]
        $Server
        
    )

    process {

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        $GetParams = @{
            Domain = $Domain
            Server = $Server
        }
        if ( $Guid ) { $GetParams.Guid = $Guid }
        elseif ( $Name ) { $GetParams.Name = $Name }

        Write-Verbose ( ConvertTo-Json $GetParams )

        # verify that the WMI filter exists
        $ExistingFilter = Get-GPWmiFilter @GetParams -ErrorAction Stop

        Write-Debug 'Stop Here'

        $LookupBy = ( 'Name', 'GUID' )[ $PSBoundParameters.ContainsKey( 'Guid' ) ]
        $LookupValue = ( $Name, $Guid.ToString('B') )[ $PSBoundParameters.ContainsKey( 'Guid' ) ]

        # verify this WMI filter isn't in use
        $LinkedGPOs = Get-GPWmiFilterLinkedGPO @GetParams

        if ( $LinkedGPOs.Count -gt 0 ) {

            Write-Verbose 'Linked Group Policies:'



            $LinkedGPOs | ForEach-Object {

                Write-Verbose ( '{0} {1}' -f $_.DisplayName, $_.Id.ToString('B') )

            }

            Write-Error ( 'WMI filter with {0} ''{1}'' in domain {2} could not be removed! It is linked to {3} Group Policies.' -f $LookupBy, $LookupValue, $Domain, $LinkedGPOs.Count )
            return

        }

        if ( $PSCmdlet.ShouldProcess( $ExistingFilter.DisplayName, 'remove' ) ) {

            try {

                $NamingContext = ([adsi]"LDAP://$Domain/RootDSE").defaultNamingContext
                $SOMContainer = [adsi]"LDAP://$Server/CN=SOM,CN=WMIPolicy,CN=System,$NamingContext"
            
                $WmiFilterObject = $SOMContainer.Children.Find( "CN=$($ExistingFilter.Id.ToString('B'))" )

                $SOMContainer.Children.Remove( $WmiFilterObject )

            } catch {

                Write-Error ( 'Failed to remove WMI filter ''{0}'' in domain {1}. Please verify that you have rights to remove WMI filters. You may also need to first enable ''Allow System Only Change'' for the domain. You can use the Set-ADSystemOnlyChange cmdlet to make this change.' -f $ExistingFilter.DisplayName, $Domain )

            }

            $SOMContainer.Dispose()

        }

    }
    
}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Get-GPWmiFilterLinkedGPO {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(

        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            DontShow,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID', 
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'Id' )]
        [guid]
        $Guid,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DC' )]
        [string]
        $Server
        
    )

    process {

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        $GetParams = @{
            Domain = $Domain
            Server = $Server
        }
        if ( $Guid ) { $GetParams.Guid = $Guid }
        elseif ( $Name ) { $GetParams.Name = $Name }

        # verify that the WMI filter exists
        $ExistingFilter = Get-GPWmiFilter @GetParams

        if ( -not $ExistingFilter ) {

            $LookupBy = ( 'Name', 'GUID' )[ $PSBoundParameters.ContainsKey( 'Guid' ) ]
            $LookupValue = ( $Name, $Guid.ToString('B') )[ $PSBoundParameters.ContainsKey( 'Guid' ) ]
            
            Write-Error ( 'WMI filter with {0} ''{1}'' could not be found in domain {2}!' -f $LookupBy, $LookupValue, $Domain )
            return

        }

        # get all linked GPOs
        $NamingContext = ([adsi]"LDAP://$Domain/RootDSE").defaultNamingContext

        $SearchFilter = "(&(objectclass=groupPolicyContainer)(gPCWQLFilter=*$($ExistingFilter.Id.ToString('B'))*))"
        $SearchRoot = [adsi]"LDAP://$Server/CN=Policies,CN=System,$NamingContext"
        
        $Searcher = [adsisearcher]::new( $SearchRoot, $SearchFilter )
        $Searcher.PropertiesToLoad.Add( 'name' ) > $null
        
        $Searcher.FindAll().ForEach({ $_.Properties.name }).ForEach({ Get-GPO -Guid $_ })

        $Searcher.Dispose()
        $SearchRoot.Dispose()

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Set-GPOWmiFilterLink {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(

        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipeline
        )]
        [Microsoft.GroupPolicy.Gpo]
        $GPO,

        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 1
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID',
            Mandatory,
            Position = 1
        )]
        [guid]
        $Guid,

        [Parameter(
            ParameterSetName = 'Clear',
            Mandatory,
            Position = 1
        )]
        [switch]
        $Clear,

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DC' )]
        [string]
        $Server,

        [switch]
        $PassThru
        
    )

    process {

        if ( -not $Server ) {
        
            $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name
        
        }

        # get the GPO as a directory entry
        $GPODirectoryEntry = Get-GPODirectoryEntry $GPO -Domain $Domain -Server $Server

        if ( $Clear ) {

            $GPODirectoryEntry.PutEx( 1, 'gPCWQLFilter', 0 )

        } else {

            # get the GPWmiFilter object
            $GetParam = @{}
            if ( $Name ) { $GetParam.Name = $Name }
            if ( $Guid ) { $GetParam.Guid = $Guid }
            $WmiFilterObject = Get-GPWmiFilter @GetParam -Domain $Domain -Server $Server -ErrorAction Stop

            # assign the value
            $GPODirectoryEntry.gPCWQLFilter = "[$Domain;$($WmiFilterObject.Id.ToString('B'));0]"

        }

        if ( $PSCmdlet.ShouldProcess( $GPO.DisplayName, 'set WMI filter' ) ) {

            try {

                $GPODirectoryEntry.CommitChanges()

            } catch {

                Write-Error ( 'Failed to update GPO ''{0}'' in domain {1}. Please verify that you have rights to edit this GPO.' -f $GPO.DisplayName, $GPO.DomainName )
                return

            }

        }

        if ( $PassThru ) {

            $GPO | Get-GPO

        }

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Get-GPODirectoryEntry {

    param(

        [Parameter(
            Mandatory,
            Position = 0,
            ValueFromPipeline
        )]
        [Microsoft.GroupPolicy.Gpo]
        $GPO,

        [Parameter(
            Mandatory,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DC' )]
        [string]
        $Server

    )

    process {

        $DirectoryContext = [DirectoryContext]::new( 'Domain', $Domain )
        
        if ( -not $Server ) {

            $Server = [Domain]::GetDomain( $DirectoryContext ).FindDomainController().Name

        }
        
        $NamingContext = ([adsi]"LDAP://$Domain/RootDSE").defaultNamingContext
        [adsi]"LDAP://$Server/CN=$($GPO.Id.ToString('B')),CN=Policies,CN=System,$NamingContext"

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Set-ADSystemOnlyChange {

    param(

        [Parameter(Mandatory, ValueFromPipeline, Position=1, ParameterSetName='Enable_Remote')]
        [Parameter(Mandatory, ValueFromPipeline, Position=1, ParameterSetName='Disable_Remote')]
        [string[]]
        $ComputerName,

        [Parameter(Mandatory, ParameterSetName='Enable_Local')]
        [Parameter(Mandatory, ParameterSetName='Enable_Remote')]
        [switch]
        $Enable,

        [Parameter(Mandatory, ParameterSetName='Disable_Local')]
        [Parameter(Mandatory, ParameterSetName='Disable_Remote')]
        [switch]
        $Disable,

        [Parameter(ParameterSetName='Enable_Remote')]
        [Parameter(ParameterSetName='Disable_Remote')]
        [pscredential]
        $Credential

    )

    begin {

        $CommandSplat = @{}

        if ( $Credential ) { $CommandSplat.Credential = $Credential }

        [ArrayList]$Computers = @()

    }

    process {

        $ComputerName.ForEach({ $Computers.Add( $_ ) > $null })

    }

    end {

        if ( $Computers.Count -gt 0 ) {

            [string[]]$CommandSplat.ComputerName = $Computers

        }

        if ( $Enable ) {

            Write-Warning "Enabling the setting 'Allow System Only Change' should be a temporary measure. Don't foreget to revert this setting once changes are applied."

        }

        Invoke-Command @CommandSplat -ScriptBlock {

            # check for NTDS service
            if ( -not( Get-Service NTDS -ErrorAction SilentlyContinue ) ) {

                throw 'Must be run on a domain controller!'

            }

            # check for and create if missing the Parameters registry key
            $ParametersKey = Get-Item -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -ErrorAction SilentlyContinue

            if ( -not $ParametersKey ) {
        
                New-Item -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -ItemType RegistryKey > $null
        
            }

            # check if 'Allow System Only Change' value exists
            $SystemOnlyChangeValue = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -Name 'Allow System Only Change' -ErrorAction SilentlyContinue

            if ( -not $SystemOnlyChangeValue ) {

                New-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -Name 'Allow System Only Change' -Value ([int]$Using:Enable.IsPresent) -PropertyType DWORD > $null

            } else {

                Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -Name 'Allow System Only Change' -Value ([int]$Using:Enable.IsPresent) > $null
            
            }

        }

    }

}


# .ExternalHelp BW.Utils.GroupPolicy.WMIFilter-help.xml
function Test-ADSystemOnlyChangeEnabled {

    [CmdletBinding(DefaultParameterSetName='Local')]
    param(

        [Parameter(Mandatory, ValueFromPipeline, Position=1, ParameterSetName='Remote')]
        [string[]]
        $ComputerName,

        [Parameter(ParameterSetName='Remote')]
        [pscredential]
        $Credential

    )

    begin {

        $CommandSplat = @{}

        if ( $Credential ) { $CommandSplat.Credential = $Credential }

        [ArrayList]$Computers = @()

    }

    process {

        $ComputerName.ForEach({ $Computers.Add( $_ ) > $null })

    }

    end {

        if ( $Computers.Count -gt 0 ) {

            [string[]]$CommandSplat.ComputerName = $Computers

        }

        Invoke-Command @CommandSplat -ScriptBlock {

            # check for NTDS service
            if ( -not( Get-Service NTDS -ErrorAction SilentlyContinue ) ) {

                throw 'Must be run on a domain controller!'

            }

            # check for and create if missing the Parameters registry key
            $ParametersKey = Get-Item -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -ErrorAction SilentlyContinue

            if ( -not $ParametersKey ) {
        
                return $false
        
            }

            # check if 'Allow System Only Change' value exists
            $SystemOnlyChangeValue = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\NTDS\Parameters' -Name 'Allow System Only Change' -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty 'Allow System Only Change'

            return [bool]$SystemOnlyChangeValue

        }

    }

}

function Test-GPWmiFilterParameterBinding {

    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(

        [Parameter(
            ParameterSetName = 'ByName',
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Parameter(
            ParameterSetName = 'ByGUID',
            DontShow,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DisplayName' )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = 'ByGUID', 
            Mandatory,
            Position = 0,
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'Id' )]
        [guid]
        $Guid,

        [string]
        $NewName,

        [string]
        $Description,

        [WmiFilterList]
        $Filters,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DomainName' )]
        [string]
        $Domain = $env:USERDNSDOMAIN,

        [Parameter(
            ValueFromPipelineByPropertyName
        )]
        [Alias( 'DC' )]
        [string]
        $Server
        
    )

    process {

        Write-Verbose "Being Called" -Verbose
        $input | %{ Write-Verbose "INPUT: $_" -Verbose }
        $args | %{ Write-Verbose "ARGS: $_" -Verbose }
        $PSBoundParameters.Keys | %{ Write-Verbose "BOUND: $_" -Verbose }

    }

}