ActiveDirectoryTools.psm1
Function Get-KerberosDelegationInformation { <# .SYNOPSIS Gets Kerberos delegation information for a delegated server and its target. .DESCRIPTION The cmdlet retrieves the delegation information and UAC information for delegation permissions. It also retrieves the registered SPNs for the target servers. .PARAMETER DelegatedServer The server that is granted Kerberos delegation permissions. .PARAMETER TargetServer The server that is the target of the delegation. .PARAMETER Credential Optional. The credentials to use to query Active Directory for the AD Computers. .EXAMPLE Get-KerberosDelegationInformation -DelegatedServer WebServer1 -TargetServer AppServer1 Gets the delegation information for WebServer1 to AppServer1. .INPUTS System.String, Systen.String, System.Management.Automation.PSCredential .OUTPUTS System.Management.Automation.PSCustomObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/27/2016 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$DelegatedServer, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$TargetServer, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [PSCredential]$Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } } Process { if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $Delegation = Get-ADComputer -Identity $DelegatedServer -Credential $Credential -Properties msDS-AllowedToDelegateTo,UserAccountControl | Select-Object -Property msDS-AllowedToDelegateTo,UserAccountControl [string[]]$SPNs = (Get-ADComputer -Identity $TargetServer -Credential $Credential -Properties ServicePrincipalName | Select-Object -ExpandProperty ServicePrincipalName).Split("`n") } else { $Delegation = Get-ADComputer -Identity $DelegatedServer -Properties msDS-AllowedToDelegateTo,UserAccountControl | Select-Object -Property msDS-AllowedToDelegateTo,UserAccountControl [string[]]$SPNs = (Get-ADComputer -Identity $TargetServer -Properties ServicePrincipalName | Select-Object -ExpandProperty ServicePrincipalName).Split("`n") } $Matches = @() foreach ($Value in $script:UACValues) { #Perform bitwise and to compare the current UAC against each possible value and record matches if ($Delegation.UserAccountControl -band [System.Convert]::ToInt64($Value.Key, 16)) { $Matches += $Value } } [PSCustomObject]$DelegationInfo = @{DelegatedServer=$DelegatedServer;AllowedToDelegateTo=$Delegation.'msDS-AllowedToDelegateTo';UAC=$Matches;TargetServer=$TargetServer;TargetSPNs=$SPNs} } End { Write-Output $DelegationInfo } } Function Import-WmiFiltersFromJson { <# .SYNOPSIS Imports WMI Filter information from a JSON file. .DESCRIPTION The cmdlet uses a backup of WMI filters written to a json file to restore each WMI filter in the provided domain. .PARAMETER Path The location of the json file containing the WMI information. This file is created with the Export-GPOBackupsAndWmiFilters cmdlet. It can also be created manually. .PARAMETER Domain The domain to import the WMI filters. This defaults to the domain of the current user. .PARAMETER Force Overwrites any existing WMI filters. .PARAMETER Credential The credential to use to create the WMI filters. .EXAMPLE Import-WmiFiltersFromJson -Path "c:\GPOBackups\WmiFilters.json" -Domain "contoso.com" Imports the WMI filters stored at c:\GPOBackups\WmiFilters.json that were backed up to that path with Export-GPOBackupsAndWmiFilters to contoso.com. .INPUTS System.String, System.String, System.Management.Automation.SwitchParameter .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/7/2015 #> Param ( [CmdletBinding()] [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Path, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [switch]$Force ) Begin { Import-Module ActiveDirectory -ErrorAction Stop } Process { if ($Domain -eq [System.String]::Empty) { $ADDomain = Get-ADDomain -Current LoggedOnUser } else { $ADDomain = Get-ADDOmain -Identity $Domain } $DomainDN = $ADDomain.DistinguishedName $DomainName = $ADDomain.DnsRoot $Server = $DomainName if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { $Server = $env:COMPUTERNAME } } $WmiPath = "CN=SOM,CN=WMIPolicy,CN=System,$DomainDN" $WmiFilters = Get-Content -Path $Path -Raw | ConvertFrom-Json foreach ($Filter in $WmiFilters) { $Attr = @{ "msWMI-Name" = $Filter."msWMI-Name"; "msWMI-Parm1" = if ($Filter."msWMI-Parm1" -ne $null) { $Filter."msWMI-Param1"} else {$Filter."msWMI-Name"}; "msWMI-Parm2" = $Filter."msWMI-Parm2"; "msWMI-Author" = $Filter."msWMI-Author"; "msWMI-ID"= $Filter."msWMI-ID"; "instanceType" = 4; "showInAdvancedViewOnly" = "TRUE"; "distinguishedname" = "CN=$($Filter."msWMI-ID"),$WmiPath"; "msWMI-ChangeDate" = $Filter."msWMI-ChangeDate"; "msWMI-CreationDate" = $Filter."msWMI-CreationDate" } $ExistingFilter try { $ExistingFilter = Get-ADObject -Identity $Attr.distinguishedname -Server $Server -ErrorAction Stop } catch [Exception] { $ExistingFilter = $null } if ($ExistingFilter -ne $null) { if ($Force) { Write-Host "Replacing $($Attr.'msWMI-Name') WMI Filter" Remove-ADObject -Identity $Attr.distinguishedname -Credential $Credential -Confirm:$false -Server $Server New-ADObject -Name $Attr."msWMI-ID" -Type "msWMI-Som" -Path $WmiPath -OtherAttributes $Attr -Server $Server } else { Write-Warning "The WMI Filter $($Attr.'msWMI-Name') with this ID already exists." } } else { Write-Host "Importing $($Attr.'msWMI-Name') WMI Filter" New-ADObject -Name $Attr."msWMI-ID" -Type "msWMI-Som" -Path $WmiPath -OtherAttributes $Attr -Server $Server } } } End {} } Function Import-GPPermissionsFromJson { <# .SYNOPSIS Imports Group Policy object permissions information from a JSON file and applies them to applicable GPOs. .DESCRIPTION The cmdlet uses a backup of GPO permissions written to a json file recreate permissions on exported GPOs. .PARAMETER Path The location of the json file containing the Group Policy permissions. This file is created with the Export-GPOBackupsAndWmiFilters cmdlet. It can also be created manually. .PARAMETER Domain The domain to import the Group Policy object permissions to. This defaults to the domain of the current user. .EXAMPLE Import-GPPermissionsFromJson -Path "c:\GPOBackups\GPPermissions.json" -Domain "contoso.com" Imports the Group Policy permissions stored at c:\GPOBackups\GPPermissions.json that were backed up to that path with Export-GPOBackupsAndWmiFilters to contoso.com. .INPUTS System.String, System.String .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/7/2015 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [ValidateScript({Test-Path -Path $_ })] [string]$Path, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty ) Begin { if ([System.String]::IsNullOrEmpty($Domain)) { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } [String[]]$Drives = Get-PSDrive | Select-Object -ExpandProperty Name $TempDrive = "tempdrive" if ($Drives.Contains($TempDrive)) { Write-Host "An existing PSDrive exists with name $TempDrive, temporarily removing" -ForegroundColor Yellow $OldDrive = Get-PSDrive -Name $TempDrive Remove-PSDrive -Name $TempDrive } $Drive = New-PSDrive -Name $TempDrive -Root "" -PSProvider ActiveDirectory -Server $Domain Push-Location -Path "$Drive`:\" $GPOs = Get-GPO -All -Domain $Domain } Process { $Permissions = ConvertFrom-Json -InputObject (Get-Content -Path $Path -Raw) foreach ($Permission in ($Permissions | Where-Object {($GPOs | Select-Object -ExpandProperty DisplayName).Contains($_.Name) })) { Write-Host "******** Processing $($Permission.Name) ********" Write-Host "" $GPO = Get-GPO -Name $Permission.Name -Domain $Domain switch ($Permission.Type) { "ACL" { Write-Host "Using ACL type permission." $Name = $Permission.Trustee.Name $ADObject = Get-ADObject -Filter {(name -eq $Name) -or (samAccountName -eq $Name)} -Properties * if ($ADObject -ne $null) { $GPOACL = Get-Acl -Path $GPO.Path foreach ($Entry in $Item.Permission) { [System.DirectoryServices.ActiveDirectoryAccessRule]$NewAce = New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule( ` $ADObject.ObjectSID, $Entry.ActiveDirectoryRights, $Entry.AccessControlType, $Entry.InheritanceType) $GPOACL.AddAccessRule($NewAce) } # Commit the ACL Set-Acl -Path $GPO.Path -AclObject $GPOACL -Passthru } else { Write-Warning "Could not find an AD Object matching $Name." } break } "GPPermission" { Write-Host "Using GPPermission type permission." $SidType =[Microsoft.GroupPolicy.SecurityIdentifierType]($Permission.Trustee.SidType) $TrusteeName = $Permission.Trustee.Name try { switch ($SidType) { ([Microsoft.GroupPolicy.SecurityIdentifierType]::Group) { if ((Get-ADGroup -Filter {name -eq $TrusteeName}) -ne $null) { Set-GPPermission -Guid $GPO.Id -PermissionLevel $Permission.Permission -Replace -TargetType $Permission.Trustee.SidType -TargetName $Permission.Trustee.Name } else { Write-Warning "Could not find a group matching $($Permission.Trustee.Name)." } break } ([Microsoft.GroupPolicy.SecurityIdentifierType]::WellKnownGroup) { Set-GPPermission -Guid $GPO.Id -PermissionLevel $Permission.Permission -Replace -TargetType ([Microsoft.GroupPolicy.Commands.PermissionTrusteeType]::Group) -TargetName $Permission.Trustee.Name break } ([Microsoft.GroupPolicy.SecurityIdentifierType]::User) { if ((Get-ADUser -Filter {(name -eq $TrusteeName) -or (displayName -eq $TrusteeName) -or (samAccountName -eq $TrusteeName)}) -ne $null) { Set-GPPermission -Guid $GPO.Id -PermissionLevel $Permission.Permission -Replace -TargetType $Permission.Trustee.SidType -TargetName $Permission.Trustee.Name } else { Write-Warning "Could not find a user matching $($Permission.Trustee.Name)." } break } ([Microsoft.GroupPolicy.SecurityIdentifierType]::Computer) { if ((Get-ADComputer -Filter {(name -eq $TrusteeName) -or (displayName -eq $TrusteeName) -or (samAccountName -eq $TrusteeName) }) -ne $null) { Set-GPPermission -Guid $GPO.Id -PermissionLevel $Permission.Permission -Replace -TargetType $Permission.Trustee.SidType -TargetName $Permission.Trustee.Name } else { Write-Warning "Could not find a computer matching $TrusteeName." } break } default { Write-Warning "The trustee type $SidType did not match an expected value of Group, WellKnownGroup, User, or Computer." break } } } catch [Exception] { Write-Warning "Set-GPPermission`n$($Permission.Name)`n$($Permission.Trustee.Name)`n$($Permission.Permission)`n$($_.Exception.Message)" } break } default { Write-Warning "The ACL type $($Permission.Type) did not match GPPermission or ACL." break } } } } End { Pop-Location Remove-PSDrive $Drive if ($OldDrive -ne $null) { Write-Host "Recreating original PSDrive" -ForegroundColor Yellow New-PSDrive -Name $OldDrive.Name -PSProvider $OldDrive.Provider -Root $OldDrive.Root | Out-Null $OldDrive = $null } } } Function Export-GPOBackupsAndWmiFilters { <# .SYNOPSIS Backups all GPOs in a given domain and writes all WMI Filter information to a JSON file. The permissions for each GPO are also written to a JSON file. .DESCRIPTION The cmdlet uses a native backup of every GPO in the domain. Then it queries all of the WMI filters and backs them up to a JSON files. The JSON file can be read later and used to restore all of the WMI filters. The permissions on each GPO are also backed up to a separate JSON file, which can be used later to set permissions on GPOs that have already been imported. .PARAMETER Path The directory location to store the backups. This directory will be created if it does not exist. .PARAMETER Domain The domain from which to backup GPOs. This defaults to the domain of the current user. .PARAMETER Credential The credential to use to perform all of the functions in the cmdlet. Requires the ability to create the folder the backups will be stored in and read Active Directory objects and attributes. .EXAMPLE Export-GPOBackupsAndWmiFilter -Path "c:\GPOBackups" -Domain "contoso.com" Exports all of the GPO backups and WMI filter information for contoso.com to the specified path. .INPUTS System.String, System.String, System.Management.Automation.PSCredential .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/7/2015 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$true,Mandatory=$true)] [string]$Path, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop Import-Module GroupPolicy -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredetial]::Empty } } Process { if ($Domain -eq [System.String]::Empty) { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } $Server = $Domain if ((Test-IsDomainAdmin -Domain $Domain) -or (Test-IsEnterpriseAdmin)) { $GPOs = Get-GPO -All -Domain $Domain foreach($GPO in $GPOs) { $BackupDirectory = ($Path + "\" + $GPO.DisplayName.Replace(",","").Replace(":","").Replace("*","")) if (!(Test-Path -Path $BackupDirectory)) { New-Item -Path $BackupDirectory -ItemType Directory -Force -Credential $Credential | Out-Null } Backup-GPO -Guid $GPO.Id -Path $BackupDirectory -Comment "Backed up on $(Get-Date)" -Server $Server -Domain $Domain } $PermissionsBackupFile = New-Item -Path "$Path\GPPermissions.json" -ItemType File -Force -Credential $Credential Export-GPOPermissions -All -Domain $Domain -Destination $PermissionsBackupFile.FullName if ($Credential -ne $null -and $Credential -ne [PSCredential]::Empty) { $WmiFilters = Get-ADObject -Filter {objectClass -eq "msWMI-Som"} -Properties "msWMI-Author","msWMI-ChangeDate","msWMI-CreationDate","msWMI-ID","msWMI-Name","msWMI-Parm1","msWMI-Parm2" -Server $Server -Credential $Credential } else { $WmiFilters = Get-ADObject -Filter {objectClass -eq "msWMI-Som"} -Properties "msWMI-Author","msWMI-ChangeDate","msWMI-CreationDate","msWMI-ID","msWMI-Name","msWMI-Parm1","msWMI-Parm2" -Server $Server } if ($WmiFilters -ne $null -and $WmiFilters.Count -gt 0) { Write-Host "Backing up WMI Filters." $WmiBackupFile = New-Item -Path "$Path\WmiFilters.json" -ItemType File -Force -Credential $Credential $WmiFilters = $WmiFilters | Select-Object -Property "msWMI-Author","msWMI-ChangeDate","msWMI-CreationDate","msWMI-ID","msWMI-Name","msWMI-Parm1","msWMI-Parm2" $Json = ConvertTo-Json -InputObject $WmiFilters $Json = $Json.Replace("null", "`"`"") Set-Content -Value $Json -Path $WmiBackupFile -Force -Credential $Credential Write-Host "Completed WMI Filter backup." } else { Write-Host "No WMI filters in the domain." } } else { throw [System.UnauthorizedAccessException]("You must be at least a Domain Admin to utilize this cmdlet.") } } End {} } Function Export-GPOPermissions { <# .SYNOPSIS Exports all of the permission information of GPOs to a JSON file. .DESCRIPTION The cmdlet uses the get Get-GPPermission command to get initial information. Any GPOs with custom permissions have their permissions enumerated via ACL. All of the permissions are written to a JSON file that can be used with Import-GPPermissionsFromJson. .PARAMETER DisplayNames The names of the GPOs whose permissions should be backed up. .PARAMETER All Specifies that all GPOs should have their permissions backed up. .PARAMETER Domain The domain from which to backup GPO permissions in. This defaults to the domain of the current user. .PARAMETER Destination The file that should be created with the permission information. If the file exists, the content will be overwritten. .EXAMPLE Export-GPOPermissions -All -Destination c:\GPPermissions.json Exports all of the GPO permissions to c:\GPPermissions.json .EXAMPLE Export-GPOPermissions -DisplayNames @("Default Domain Policy","Default Domain Controllers Policy") -Destination c:\GPPermissions.json Exports the permissions for the Default Domain Policy and Default Domain Controllers Policy to c:\GPPermissions.json .INPUTS System.String, System.String, System.Management.Automation.PSCredential .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 3/18/2016 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName="Names",Mandatory=$true)] [ValidateScript({$_.Count -gt 0})] [string[]]$DisplayNames, [Parameter(Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName="All",Mandatory=$true)] [switch]$All, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Destination ) Begin { if ([System.String]::IsNullOrEmpty($Domain)) { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } $GPOs = @() if ($All) { $GPOs += Get-GPO -All } else { foreach ($Name in $DisplayNames) { $GPOs += Get-GPO -Name $Name } } [PSCustomObject[]]$ACLs = @() [String[]]$Drives = Get-PSDrive | Select-Object -ExpandProperty Name $TempDrive = "tempdrive" if ($Drives.Contains($TempDrive)) { Write-Host "An existing PSDrive exists with name $TempDrive, temporarily removing" -ForegroundColor Yellow $OldDrive = Get-PSDrive -Name $TempDrive Remove-PSDrive -Name $TempDrive } $Drive = New-PSDrive -Name $TempDrive -Root "" -PSProvider ActiveDirectory -Server $Domain Push-Location -Path "$Drive`:\" } Process { foreach ($GPO in $GPOs) { Write-Host "Processing $($GPO.DisplayName) permissions." $Permissions = Get-GPPermission -Name $GPO.DisplayName -All foreach ($Permission in $Permissions) { $Trustee = [PSCustomObject]@{"Domain" = $Permission.Trustee.Domain; "DSPath" = $Permission.Trustee.DSPath; "Name" = $Permission.Trustee.Name; "Sid" = $Permission.Trustee.Sid; "SidType" = $Permission.Trustee.SidType} if ($Permission.Permission -eq [Microsoft.GroupPolicy.GPPermissionType]::GpoCustom) { [System.DirectoryServices.ActiveDirectoryAccessRule[]]$ACL = Get-Acl -Path "$($GPO.Path)" | Select-Object -ExpandProperty Access | Where-Object {$_.IdentityReference.ToString().Split("\\")[-1] -eq $Permission.Trustee.Name } $Rights = @() foreach ($ACE in $ACL) { $Rights += [PSCustomObject]@{"ActiveDirectoryRights" = $ACE.ActiveDirectoryRights; "InheritanceType" = $ACE.InheritanceType; "AccessControlType" = $ACE.AccessControlType; "ObjectType" = $ACE.ObjectType; "InheritanceFlags" = $ACE.InheritanceFlags} } [PSCustomObject]$Entry = @{"Type" = "ACL";"Name" = $GPO.DisplayName; "Trustee" = $Trustee; "GPODistinguishedName" = $GPO.Path; "Permission" = $Rights; "Denied" = if ($ACE.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Deny) {$true} else {$false}} } else { [PSCustomObject]$Entry = @{"Type" = "GPPermission"; "Name" = $GPO.DisplayName; "Trustee" = $Trustee; "GPODistinguishedName" = $GPO.Path; "Permission" = $Permission.Permission; "Denied" = $Permission.Denied} } $ACLs += $Entry } } } End { Pop-Location Remove-PSDrive $Drive if ($OldDrive -ne $null) { Write-Host "Recreating original PSDrive" -ForegroundColor Yellow New-PSDrive -Name $OldDrive.Name -PSProvider $OldDrive.Provider -Root $OldDrive.Root | Out-Null $OldDrive = $null } if ([System.String]::IsNullOrEmpty($Destination)) { Write-Output $ACLs } else { Set-Content -Path $Destination -Value (ConvertTo-Json -InputObject $ACLs -Depth 5) -Force -Confirm:$false } } } Function Import-FullGPOBackups { <# .SYNOPSIS Restores all most recent GPO backups stored under a given path and reassociates WMI filters to the GPO. .DESCRIPTION The cmdlet recursively searches the provided path for GPO backups. Once a backup is found, it is imported into the provided domain. After the import, the GPO Report XML is utilized to identify any WMI filters that were used, and reassociates those WMI filters to the GPO if they exist in the domain. This command is useful for restoring GPOs that were backed up natively and restoring them to a new domain after the WMI filters have been imported. .PARAMETER Path The base path to start searching from. .PARAMETER Domain The domain to import the GPOs into. This defaults to the domain of the current user. .PARAMETER MigrationTablePath Optionally specify a migration table to be used to conduct the GPO import .EXAMPLE Import-FullGPOBackups -Path "c:\GPOBackups" -Domain "contoso.com" Import all of the backups contained in the folder structure to contoso.com and associate any WMI filters. .INPUTS System.String, System.String, System.String .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 3/24/2016 #> [CmdletBinding()] Param ( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Path, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [ValidateScript({Test-Path -Path $_})] [string]$MigrationTablePath = [System.String]::Empty ) Begin { Import-Module GroupPolicy -ErrorAction Stop } Process { if ($Domain -eq [System.String]::Empty) { $DomainObject = Get-ADDomain -Current LoggedOnUser } else { $DomainObject = Get-ADDomain -Identity $Domain } $ADDomain = $DomainObject.DnsRoot $Server = $ADDomain $DomainDN = $DomainObject.DistinguishedName Write-Host "Importing GPOs to $DomainDN." $DomainController = [System.String]::Empty if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { $Server = $env:COMPUTERNAME } } $WmiFilters = Get-ADObject -Filter 'objectClass -eq "msWMI-SOM"' -Properties "msWMI-Author","msWMI-ChangeDate","msWMI-CreationDate","msWMI-ID","msWMI-Name","msWMI-Parm1","msWMI-Parm2" -Server $Server if ($WmiFilters -eq $null) { $WmiFilters = @() } $Directories = Get-ChildItem -Path $Path -Recurse -Directory | Select-Object -ExpandProperty FullName foreach ($Directory in $Directories) { [Microsoft.GroupPolicy.BackupDirectory]$BackupDirectory = New-Object Microsoft.GroupPolicy.BackupDirectory($Directory, [Microsoft.GroupPolicy.BackupType]::Gpo) [Microsoft.GroupPolicy.GPSearchCriteria]$SearchCriteria = New-Object Microsoft.GroupPolicy.GPSearchCriteria $SearchCriteria.Add([Microsoft.GroupPolicy.SearchProperty]::MostRecentBackup, [Microsoft.GroupPolicy.SearchOperator]::Equals, $true) [Microsoft.GroupPolicy.GpoBackupCollection]$Backups = $BackupDirectory.SearchGpoBackups($SearchCriteria) if ($Backups -ne $null -and $Backups.Count -gt 0) { foreach($Backup in $Backups) { Write-Host "Importing $($Backup.BackupDirectory)" try { if (![System.String]::IsNullOrEmpty($MigrationTablePath)) { $Gpo = Import-GPO -Path $Backup.BackupDirectory -BackupGpoName $Backup.DisplayName -CreateIfNeeded -MigrationTable $MigrationTablePath -Server $Server -Domain $ADDomain -TargetName $Backup.DisplayName -ErrorAction Stop Write-Host "Successfully imported $($Gpo.DisplayName) to $($Gpo.DomainName)." } else { $Gpo = Import-GPO -Path $Backup.BackupDirectory -BackupGpoName $Backup.DisplayName -CreateIfNeeded -Server $Server -Domain $ADDomain -TargetName $Backup.DisplayName -ErrorAction Stop Write-Host "Successfully imported $($Gpo.DisplayName) to $($Gpo.DomainName)." } } catch [Exception] { Write-Warning $_.Exception.Message Write-Warning ($_.CategoryInfo.Category.ToString()) Write-Warning ($_.InvocationInfo | Format-List | Out-String) Write-Warning ($_.ScriptStackTrace | Format-List | Out-String) break } $Counter = 0 while ($true) { try { $Temp = Get-ADObject -Identity "CN={$($Gpo.Id)},CN=Policies,CN=System,$DomainDN" -ErrorAction SilentlyContinue break } catch [Exception] { Write-Host "Waiting for GPO $($Gpo.DisplayName) : $($Gpo.Id) to finish creation..." Start-Sleep -Seconds 1 $Counter++ if ($Counter -gt 60) { Write-Warning "Timeout waiting for the GPO to finish creation." $Gpo = $null break } } } if ($WmiFilters.Count -gt 0) { [Xml]$GpoReport = $Backup.GenerateReport([Microsoft.GroupPolicy.ReportType]::Xml) $Namespace = New-Object System.Xml.XmlNamespaceManager($GpoReport.NameTable) $Namespace.AddNamespace("ns", $GpoReport.DocumentElement.NamespaceURI) [bool]$FilterDataAvailable = ($GpoReport.SelectSingleNode("//ns:FilterDataAvailable", $Namespace)).InnerText if ($Gpo -ne $null -and $FilterDataAvailable) { $WmiFilterName = ($GpoReport.SelectSingleNode("//ns:FilterName", $Namespace)).InnerText if (![System.String]::IsNullOrEmpty($WmiFilterName) -and $WmiFilters."msWMI-Name".Contains($WmiFilterName)) { $WmiObject = $WmiFilters | Where-Object {$_."msWMI-Name" -eq $WmiFilterName} | Select-Object -First 1 if ($WmiObject -ne $null) { $WmiFilterId = $WmiObject."msWMI-ID" Write-Host "Using WMI Filter ID: $WmiFilterId for: `nCN={$($Gpo.Id)},CN=Policies,CN=System,$DomainDN" try { Set-ADObject -Identity "CN={$($Gpo.Id)},CN=Policies,CN=System,$DomainDN" -Replace @{gPCWQLFilter = "[$ADDomain;$WmiFilterId;0]"} -Server $Server Write-Host "Added existing WMI Filter $WmiFilterName to the GPO." } catch [Exception] { Write-Warning $_.Exception.Message Write-Warning ($_.CategoryInfo.Category.ToString()) Write-Warning ($_.InvocationInfo | Format-List | Out-String) Write-Warning ($_.ScriptStackTrace | Format-List | Out-String) } } else { Write-Warning "Could not find the wmi filter even though the filter list contained a match." } } } } } } } } End {} } Function Get-WmiFilter { <# .SYNOPSIS Gets WMI Filters in the domain. .DESCRIPTION The Get-WmiFilter cmdlet returns a specific WMI Filter object based on a name or GUID or returns all WMI Filters. Optionally, link information for where each WMI filter is applied can be returned. .PARAMETER GUID The GUID of the WMI Filter to get. .PARAMETER Domain The domain to perform the search on. This defaults to the domain of the current user. .PARAMETER All Returns all WMI filters in the domain. .PARAMETER Name The "msWMI-Name" attribute of the WMI Filter to match. .PARAMETER IncludeLinkInformation If specified, information about where each WMI filter is applied is returned as well. .PARAMETER Credential The credential to use to connect to Active Directory to retrieve the object information. .EXAMPLE Get-WmiFilter -All -Domain contoso.com Gets all of the WMI Filters in contoso.com .EXAMPLE Get-WmiFilter -All -IncludeLinkInformation Gets all of the WMI filters and their link information for the domain of the current user. .INPUTS System.Guid, System.String System.String, System.String System.Management.Automation.SwitchParameter, System.String .OUTPUTS System.Management.Automation.PSObject[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 3/24/2016 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true,ParameterSetName="Guid")] [System.Guid]$Guid, [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true,ParameterSetName="Name")] [string]$Name, [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true,ParameterSetName="All")] [switch]$All, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=2)] [switch]$IncludeLinkInformation, [Parameter(Position=3)] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { if ([System.String]::IsNullOrEmpty($Domain)) { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } } Process { $WmiFilters = @() switch ($PSCmdlet.ParameterSetName) { "Guid" { [string]$Guid = "{" + $Guid.ToString() + "}" $Filter = {(objectClass -eq "msWMI-SOM") -and (msWMI-ID -eq $Guid)} break } "Name" { $Filter = {(objectClass -eq "msWMI-SOM") -and (msWMI-Name -eq $Name)} break } "All" { $Filter = {objectClass -eq "msWMI-SOM"} break } default { throw "Could not determine parameter set name." } } [PSObject[]]$WmiFilters = Get-ADObject -Filter $Filter -Properties "msWMI-Author","msWMI-ChangeDate","msWMI-CreationDate","msWMI-ID","msWMI-Name","msWMI-Parm1","msWMI-Parm2","DistinguishedName" -Server $Domain if ($IncludeLinkInformation) { $GPOs = Get-ADObject -Filter {objectClass -eq "groupPolicyContainer"} -Properties "gPCWQLFilter","displayName","name" | Where-Object {![System.String]::IsNullOrEmpty($_.gPCWQLFilter)} | Select-Object -Property "gPCWQLFilter","displayName","name" foreach ($Filter in $WmiFilters) { Add-Member -InputObject $Filter -MemberType NoteProperty -Name "LinkInformation" -Value @() -TypeName [System.Management.Automation.PSObject[]] -Force $Filter.LinkInformation += ($GPOs | Where-Object {$_.gPCWQLFilter.Split(";")[1] -eq $Filter.'msWMI-ID'} | Select-Object "DisplayName","name") } } } End { Write-Output $WmiFilters } } Function Get-GroupMembershipChanges { <# .SYNOPSIS Finds all of the group membership changes in the logs on all of the domain controllers in a domain. .DESCRIPTION Finds all of the group membership changes in the logs on all of the domain controllers in a domain. It returns these logs in several formats that can be selected. The default output is a custom object. .PARAMETER DaysAgo Specifies how many days in the past the search should start. .PARAMETER Domain The domain to perform the search on. This defaults to the domain of the current user. .PARAMETER AsJson Returns the report as a Json string. .PARAMETER AsXml Returns the report as an Xml string. .PARAMETER AsHtml Returns the report as a custom formatted html document. .PARAMETER Credential The credentials to use to perform the search and get the event log data. .EXAMPLE Get-GroupMembershipChanges -DaysAgo 1 -Domain "contoso.com" Gets all of the group membership changes in the past day from contoso.com .INPUTS System.Int32, System.String .OUTPUTS System.String, System.Management.Automation.PSObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 3/18/2016 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] [int]$DaysAgo = 1, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(ParameterSetName="Json")] [switch]$AsJson, [Parameter(ParameterSetName="Xml")] [switch]$AsXml, [Parameter(ParameterSetName="Html")] [switch]$AsHtml, [Parameter()] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop } Process { if ($Domain -eq [System.String]::Empty) { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain, $Domain) $Report = New-Object -TypeName PSObject -Property @{"Added To Domain Local Group"=@();"Removed From Domain Local Group"=@();"Added To Global Group"=@();"Removed From Global Group"=@();"Added To Universal Group"=@();"Removed From Universal Group"=@()} <# Event ID 636 & 4732 - Member added to domain local group Event ID 637 & 4733 - Member removed from domain local group Event ID 632 & 4728 - Member added to global group Event ID 633 & 4729 - Member removed from domain local group Event ID 660 & 4756 - Member added to universal group Event ID 661 & 4757 - Member removed from universal group #> $Count = 0 [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context) | ForEach-Object { $Count++ [System.Diagnostics.Eventing.Reader.EventLogRecord]$Events = Get-WinEvent -ComputerName $_.Name -FilterHashtable @{LogName=@("Security");ID=@(636,4732,637,4733,632,4728,633,4729,660,4756,661,4757);StartTime=(Get-Date).AddDays($DaysAgo * -1)} -ErrorAction SilentlyContinue #Domain Local Groups $Report."Added To Domain Local Group" += $Events | Where-Object {$_.ID -eq 636 -or $_.ID -eq 4732} | Select-Object MachineName,TimeCreated,Message $Report."Removed From Domain Local Group" += $Events | Where-Object {$_.ID -eq 637 -or $_.ID -eq 4733} | Select-Object MachineName,TimeCreated,Message #Global Groups $Report."Added To Global Group" += $Events | Where-Object {$_.ID -eq 632 -or $_.ID -eq 4728} | Select-Object MachineName,TimeCreated,Message $Report."Removed From Global Group" += $Events | Where-Object {$_.ID -eq 633 -or $_.ID -eq 4729} | Select-Object MachineName,TimeCreated,Message #Universal Groups $Report."Added To Universal Group" += $Events | Where-Object {$_.ID -eq 660 -or $_.ID -eq 4756} | Select-Object MachineName,TimeCreated,Message $Report."Removed From Universal Group" += $Events | Where-Object {$_.ID -eq 661 -or $_.ID -eq 4757} | Select-Object MachineName,TimeCreated,Message } } End { switch ($PSCmdlet.ParameterSetName) { "Json" { Write-Output (ConvertTo-Json -InputObject $Report) break } "Xml" { Write-Output (ConvertTo-Xml -InputObject $Report -Depth 2 -As String) break } "Html" { $Date = (Get-Date).ToString() $HtmlString = @" <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Group Membership Change Report</title> </head> <style> .logtable { width:100%; table-layout:fixed; border:1px solid black; } .timecolumn { width:25%; } .messagecolumn { width:75%; } .logtable td { word-break:break-all; word-wrap:break-word; vertical-align:top; text-align:left; } .logtable th { text-align:left; } </style> <body style=`"width:900px;margin-left:auto;margin-right:auto;`"> <div> Generated on $Date </div> <div> <div> <label style=`"margin:0px;`">Domain:</label><span style=`"margin-left:5px;`">$Domain</span> </div> <div> <label style=`"margin:0px;`">Number Of Domain Controllers:</label><span style=`"margin-left:5px;`">$Count</span> </div> </div> <div> "@ foreach ($Property in (Get-Member -InputObject $Report -MemberType NoteProperty)) { $HtmlString += "<h2 style=`"text-align:center`">" + $Property.Name + "</h2>" $Grouping = $Report | Select-Object -ExpandProperty $Property.Name | Group-Object -Property MachineName foreach($Group in $Grouping) { $HtmlString += "<h3 style=`"color:red;`">DOMAIN CONTROLLER: " + $Group.Name + "</h3><br />" $HtmlString += "<table class=`"logtable`"><thead><tr><th class=`"timecolumn`">Time Created</th><th class=`"messagecolumn`">Message</th></tr></thead><tbody>" foreach($LogEntry in $Group.Group) { $HtmlString += "<tr><td>" + $LogEntry.TimeCreated + "</td><td>" + $LogEntry.Message + "</td>" } $HtmlString += "</tbody></table>" } } $HtmlString += "</div></body></html>" Write-Output $HtmlString break } default { Write-Output $Report } } } } Function Get-UserObjectChangedLog { <# .SYNOPSIS Finds when a particular object's property was last modified and where the change took place. Then it searches the logs on the server. .DESCRIPTION The cmdlet finds out when an object's property was changed and what server it occured on so those logs can be searched. .PARAMETER UserName The identity of the user to find information about. This can be the SAM Account Name, Display Name, Name, or CN. .PARAMETER Property The property to search on to find when it was changed. .PARAMETER GetLog Indicates whether to try and retrieve any available log files from the server matching the change. Defaults to true. .PARAMETER Credential The credentials to use to make the search and get the event log data. .PARAMETER PassThru If specified, will return the log object and not just display data. .EXAMPLE Get-UserObjectChangedLog -UserName "john.smith" -Property "UserAccountControl" Gets log information about when the UAC was changed for john.smith .INPUTS System.String, System.String, System.Management.Automation.PSCredential .OUTPUTS System.Diagnostics.Eventing.Reader.EventLogRecord, System.Management.Automation.PSObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/7/2015 #> [CmdletBinding(DefaultParameterSetName="Powershell")] Param ( [Parameter(Position=0,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [string]$UserName, [Parameter(Position=1,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [string]$Property, [Parameter(ParameterSetName="Powershell")] [switch]$UseActiveDirectoryPowershell, [Parameter(ParameterSetName="DotNet")] [switch]$UseNativeDotNet, [Parameter()] [bool]$GetLog = $true, [Parameter(Position=2,ValueFromPipeLineByPropertyName=$true)] [PSCredential]$Credential = [PSCredential]::Empty, [Parameter(ValueFromPipeLineByPropertyName=$true)] [switch]$PassThru ) Begin {} Process { $ADUser = "" if ($Credential -ne [PSCredential]::Empty) { $ADUser = (Get-ADUser -Filter {CN -eq $UserName -or SamAccountName -eq $UserName -or Name -eq $UserName -or DisplayName -eq $UserName} -Credential $Credential).DistinguishedName } else { $ADUser = (Get-ADUser -Filter {CN -eq $UserName -or SamAccountName -eq $UserName -or Name -eq $UserName -or DisplayName -eq $UserName}).DistinguishedName } if ($ADUser -ne [System.String]::Empty) { [System.DirectoryServices.ActiveDirectory.DomainController]$DC = [System.DirectoryServices.ActiveDirectory.DomainController]::FindOne([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain) if ($PSCmdlet.ParameterSetName -eq "Powershell") { [Microsoft.ActiveDirectory.Management.ADReplicationAttributeMetadata[]]$ReplicationMetadata = Get-ADReplicationAttributeMetadata -Object $ADUser -ShowAllLinkedValues -Server $DC.Name [Microsoft.ActiveDirectory.Management.ADReplicationAttributeMetadata]$Data = $ReplicationMetadata | Where-Object {$_.AttributeName -eq $Property} [string]$OriginatingServer = $Data.Server [DateTime]$OriginatingTime = $Data.LastOriginatingChangeTime } else { [System.DirectoryServices.ActiveDirectory.ActiveDirectoryReplicationMetadata]$ReplicationMetadata = $DC.GetReplicationMetaData($ADUser) [System.DirectoryServices.ActiveDirectory.AttributeMetadata]$Data = $ReplicationMetadata.Values | Where-Object {$_.Name -eq $Property} [string]$OriginatingServer = $Data.OriginatingServer [DateTime]$OriginatingTime = $Data.LastOriginatingChangeTime } Write-Host "Originating Server: $OriginatingServer" Write-Host "Originating Time: $OriginatingTime" if ($GetLog) { Write-Warning "Checking logs on $OriginatingServer, this could take awhile..." Write-Warning "Consider running this as a job: `$Job = Start-Job -ScriptBlock {Get-UserObjectChangedLog -UserName `$args[0] -Property `$args[1]} -ArgumentList `"$UserName`",`"$Property`"" #Event Id 4738 - A User Account Was Changed [System.Diagnostics.Eventing.Reader.EventLogRecord]$Log = Get-WinEvent -ComputerName $OriginatingServer -MaxEvents 1 -FilterHashtable @{LogName=@("Security");ID=@(4738);StartTime=$OriginatingTime;EndTime=$OriginatingTime} -ErrorAction SilentlyContinue if ($Log -ne $null -and $Log.Count -ge 1) { Write-Host $Log.Message if ($PassThru) { Write-Output $Log } } else { Write-Warning "No log files could be retrieved." } } else { if ($PassThru) { Write-Output (New-Object -TypeName PSObject -Property @{"Originating Server"=$OriginatingServer;"Originating Time"=$OriginatingTime}) } } } else { Write-Error "Could not retrieve the specified user, $UserName, from Active Directory." } } End {} } Function Get-WhenMemberAddedToGroup { <# .SYNOPSIS Uses replication metadata to find when a member was added to a group. .DESCRIPTION The cmdlet gathers the replication metadata to find when a member was added to a group. This data can be used to search the logs on the identified Domain Controller to find who added the member. .PARAMETER GroupMember The Identity of the group member that was added. This can be the SAM Account Name, Display Name, Name, or CN. .PARAMETER Group The Identity of the group. This can be the SAM Account Name, Display Name, Name, or CN. .PARAMETER GetLog Indicates whether to try and retrieve any available log files from the server matching the change. Defaults to true. .PARAMETER UseActiveDirectoryPowershell Uses the Active Directory Powershell module to retrieve replication metadata. The default selection. .PARAMETER UseRepAdmin Uses the repadmin command line function and parses the output. .EXAMPLE Get-WhenMemberAddedToGroup -GroupMember Administrator -Group "Account Operators" -GetLog Writes out the server and time the member was added and attempts to retrieve the log file from the specified server for additional information which is written out to the pipeline. .INPUTS System.String, System.String .OUTPUTS System.Management.Automation.PSObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/7/2015 #> [CmdletBinding(DefaultParameterSetName="Powershell")] Param ( [Parameter(Position = 0, Mandatory = $true,ValueFromPipeLineByPropertyName=$true)] [string]$GroupMember, [Parameter(Position = 1, Mandatory = $true,ValueFromPipeLineByPropertyName=$true)] [string]$Group, [Parameter()] [bool]$GetLog = $true, [Parameter(ParameterSetName="Powershell")] [switch]$UseActiveDirectoryPowershell, [Parameter(ParameterSetName="RepAdmin")] [switch]$UseRepAdmin ) Begin { Import-Module ActiveDirectory -ErrorAction Stop } Process { [string]$ADGroupMember = (Get-ADObject -Filter {CN -eq $GroupMember -or SamAccountName -eq $GroupMember -or Name -eq $GroupMember -or DisplayName -eq $GroupMember}).DistinguishedName [string]$ADGroup = (Get-ADGroup -Filter {CN -eq $Group -or SamAccountName -eq $Group -or Name -eq $Group -or DisplayName -eq $Group}).DistinguishedName [System.DirectoryServices.ActiveDirectory.DomainController]$DC = [System.DirectoryServices.ActiveDirectory.DomainController]::FindOne([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain) if ($ADGroup -ne [System.String]::Empty) { if ($ADGroupMember -ne [System.String]::Empty) { if ($PSCmdlet.ParameterSetName -eq "RepAdmin") { $ReplicationMetaData = repadmin /showmeta $ADGroup $DC.Name #List only returns the first match #Context specifies the number of lines before and after that are returned around the matching line [string]$Line = $ReplicationMetaData | Select-String -Pattern $ADGroupMember -List -Context 2,0 $Content = $Line.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries) [string]$OriginatingServer = $Content[4].Substring($Content[4].IndexOf("\") + 1).Trim() [DateTime]$OriginatingTime = Get-Date($Content[2] + " " + $Content[3]) } if ($PSCmdlet.ParameterSetName -eq "Powershell") { [Microsoft.ActiveDirectory.Management.ADReplicationAttributeMetadata[]]$ReplicationMetadata = Get-ADReplicationAttributeMetadata -Object $ADGroup -ShowAllLinkedValues -Server $DC.Name -Properties * [Microsoft.ActiveDirectory.Management.ADReplicationAttributeMetadata]$Result = $ReplicationMetaData | Where-Object {$_.AttributeName -eq "member" -and $_.AttributeValue -eq $ADGroupMember} [DateTime]$OriginatingTime = $Result.LastOriginatingChangeTime [string]$OriginatingServer = $Result.Server } Write-Host "Originating Server: $OriginatingServer" Write-Host "Originating Time: $OriginatingTime" if ($GetLog) { Write-Warning "Checking logs on $OriginatingServer, this could take awhile..." Write-Warning "Consider running this as a job: `$Job = Start-Job -ScriptBlock {Get-WhenMemberAddedToGroup -Group `$args[0] -GroupMember `$args[1]} -ArgumentList `"$Group`",`"$GroupMember`"" # Event Id 4732 - Member added to a domain local group # Event Id 4728 - Member added to a global group # Event Id 4756 - Member added to a universal group [System.Diagnostics.Eventing.Reader.EventLogRecord]$Log = Get-WinEvent -ComputerName $OriginatingServer -MaxEvents 1 -FilterHashtable @{LogName=@("Security");ID=@(4732,4728,4756);StartTime=$OriginatingTime;EndTime=$OriginatingTime} -ErrorAction SilentlyContinue if ($Log -ne $null -and $Log.Count -ge 1) { Write-Host $Log.Message if ($PassThru) { Write-Output $Log } } else { Write-Warning "No log files could be retrieved." } } } else { Write-Error "Could not find the specified group member: $GroupMember" } } else { Write-Error "Could not find the specified group: $Group" } } End { if ($PassThru) { Write-Output (New-Object PSObject -Property @{"Originating Server"=$OriginatingServer;"Originating Time"=$OriginatingTime}) } } } Function New-StandardGPOWmiFilters { <# .SYNOPSIS Creates a standard set of WMI filters. .DESCRIPTION The cmdlet creates a set of WMI filters for each domain in the forest, or a particular domain. Some of the software filters use custom WMI classes Win32_Software and Win32_Software64 that can be installed through the Set-Win32Software script on PowerShellGallery. .PARAMETER CurrentDomainOnly Specifies that the WMI filters are only created in the current user domain. Otherwise they are created in every domain in the forest. .PARAMETER Domain The domain to create the WMI filters in. .PARAMETER WithReplace If a WMI filters exists with the same name as ones in the standard set, the WMI query expression is updated. .EXAMPLE New-StandardGPOWmiFilters Creates standard WMI Filters in every domain in the current forest. The user must be an enterprise admin for this command to succeed. .EXAMPLE New-StandardGPOWmiFilters -Domain contoso.com Creates standard WMI Filters in the contoso.com domain .EXAMPLE New-StandardGPOWmiFilters -CurrentDomainOnly Creates standard WMI Filters in the current domain of the user. The user must be a domain admin for this command to succeed. .INPUTS System.String .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> [CmdletBinding(DefaultParameterSetName="SpecifyDomain")] Param( [Parameter(Mandatory=$true,Position=0,ParameterSetName="Forest")] [switch]$AddToForest, [Parameter(Position=0,ParameterSetName="SpecifyDomain",ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true)] [string]$Domain = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [switch]$WithReplace, [Parameter(Position=2)] [PSCredential]$Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter()] [switch]$PassThru ) Begin{ $Domains = @() if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } switch ($PSCmdlet.ParameterSetName) { "Forest" { if (!(Test-IsEnterpriseAdmin -Credential $Credential)) { throw "The user must be an enterprise admin to create filters in every domain." } else { $Domains += (Get-ADForest -Current LoggedOnUser).Domains } break } "SpecifyDomain" { if ([System.String]::IsNullOrEmpty($Domain)) { $Domain = Get-ADDomain -Current LoggedOnUser | Select-Object -ExpandProperty DnsRoot } if (!(Test-IsDomainAdmin -Domain $Domain -Credential $Credential)) { throw "The user must be a domain admin to create filters in the specified domain." } else { $Domains += $Domain } break } default { throw "Could not determine parameter set." } } } Process { #region Filters $Filters = @() #region AD and NON AD Management Workstations $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=1 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType=1 "@ $ADManagementWorkstation=@{"Expression"=@($Expression,$Expression2);"Description"="Active Directory Management Workstation (Item Level Targetting)";"Name"="Active Directory Management Workstation (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $ADManagementWorkstation $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=0 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType=1 "@ $NonADManagementWorkstation=@{"Expression"=@($Expression,$Expression2);"Description"="Non Active Directory Management Workstation (Item Level Targetting)";"Name"="Non Active Directory Management Workstation (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $NonADManagementWorkstation $ADMgmtWorkstations = @( @{"Version" = "6.0."; "Name" = "Windows 7"}, @{"Version" = "6.1."; "Name" = "Windows Vista"}, @{"Version" = "6.2."; "Name" = "Windows 8"}, @{"Version" = "6.3."; "Name" = "Windows 8.1"}, @{"Version" = "10.0"; "Name" = "Windows 10"} ) foreach ($Item in $ADMgmtWorkstations) { $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=1 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND ProductType=1 "@ $ADMgmt=@{"Expression"=@($Expression,$Expression2);"Description"="$($Item.Name) - Active Directory Management (Item Level Targetting)";"Name"="$($Item.Name) - Active Directory Management (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $ADMgmt $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=0 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND ProductType=1 "@ $NonADMgmt=@{"Expression"=@($Expression,$Expression2);"Description"="$($Item.Name) - Non Active Directory Management (Item Level Targetting)";"Name"="$($Item.Name) - Non Active Directory Management (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $NonADMgmt } #endregion #region AD and NON AD Management Servers $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=1 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE (ProductType=2 OR ProductType=3) "@ $ADManagementServer=@{"Expression"=@($Expression,$Expression2);"Description"="Active Directory Management Server (Item Level Targetting)";"Name"="Active Directory Management Server (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $ADManagementServer $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=0 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE (ProductType=2 OR ProductType=3) "@ $NonADManagementServer=@{"Expression"=@($Expression,$Expression2);"Description"="Non Active Directory Management Server (Item Level Targetting)";"Name"="Non Active Directory Management Server (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $NonADManagementServer $ADMgmtServers = @( @{"Version" = "6.0."; "Name" = "Windows Server 2008"}, @{"Version" = "6.1."; "Name" = "Windows Server 2008 R2"}, @{"Version" = "6.2."; "Name" = "Windows Server 2012"}, @{"Version" = "6.3."; "Name" = "Windows Server 2012 R2"}, @{"Version" = "10.0"; "Name" = "Windows Server 2016"} ) foreach ($Item in $ADMgmtServers) { $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=1 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND ProductType=3 "@ $ADMgmt=@{"Expression"=@($Expression,$Expression2);"Description"="$($Item.Name) - Active Directory Management (Item Level Targetting)";"Name"="$($Item.Name) - Active Directory Management (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $ADMgmt $Expression = @" SELECT * FROM Win32_Environment WHERE Name = "isADManagementSystem" and VariableValue=0 "@ $Expression2 = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND ProductType=3 "@ $NonADMgmt=@{"Expression"=@($Expression,$Expression2);"Description"="$($Item.Name) - Non Active Directory Management (Item Level Targetting)";"Name"="$($Item.Name) - Non Active Directory Management (Item Level Targetting)";"Namespace"="ROOT\CIMV2";} $Filters += $NonADMgmt } #endregion #region Web Browsers $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files\\Internet Explorer\\" AND filename="iexplore" AND extension="exe" AND version>="10" AND version<"11" "@ $IE10 = @{"Expression"=@($Expression);"Description"="Internet Explorer 10";"Name"="Internet Explorer 10";"Namespace"="ROOT\CIMV2";} $Filters += $IE10 $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files\\Internet Explorer\\" AND filename="iexplore" AND extension="exe" AND version>="11" AND version<"12" "@ $IE11 = @{"Expression"=@($Expression);"Description"="Internet Explorer 11";"Name"="Internet Explorer 11";"Namespace"="ROOT\CIMV2";} $Filters += $IE11 $Expression = @" SELECT * FROM win32_software WHERE DisplayName = "Google Chrome" "@ $GoogleChrome = @{"Expression"=@($Expression);"Description"="Google Chrome";"Name"="Google Chrome";"Namespace"="ROOT\CIMV2";} $Filters += $GoogleChrome $Expression = @" SELECT * from win32_software where DisplayName like "%Firefox%" "@ $MozillaFirefox = @{"Expression"=@($Expression);"Description"="Mozilla Firefox";"Name"="Mozilla Firefox";"Namespace"="ROOT\CIMV2";} $Filters += $MozillaFirefox #endregion #region Operating Systems $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType = "2" OR ProductType = "3" "@ $WindowsServer = @{"Expression"=@($Expression);"Description"="Windows Server";"Name"="Windows Server";"Namespace"="ROOT\CIMV2";} $Filters += $WindowsServer $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType = "3" "@ $MemberServer = @{"Expression"=@($Expression);"Description"="Member Server";"Name"="Member Server";"Namespace"="ROOT\CIMV2";} $Filters += $MemberServer $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType = "2" "@ $DomainController = @{"Expression"=@($Expression);"Description"="Domain Controller";"Name"="Domain Controller";"Namespace"="ROOT\CIMV2";} $Filters += $DomainController $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE ProductType = "1" "@ $WindowsClient = @{"Expression"=@($Expression);"Description"="Windows Client";"Name"="Windows Client";"Namespace"="ROOT\CIMV2";} $Filters += $WindowsClient $Workstations = @( @{"Version" = "6.0."; "Name" = "Windows 7"}, @{"Version" = "6.1."; "Name" = "Windows Vista"}, @{"Version" = "6.2."; "Name" = "Windows 8"}, @{"Version" = "6.3."; "Name" = "Windows 8.1"}, @{"Version" = "10.0"; "Name" = "Windows 10"} ) foreach ($Item in $Workstations) { $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND ProductType = "1" "@ $Workstation = @{"Expression"=@($Expression);"Description"="$($Item.Name)";"Name"="$($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Workstation } $Servers = @( @{"Version" = "6.0."; "Name" = "Windows Server 2008"; "ProductType" = "(ProductType = `"3`" OR ProductType = `"2`")" }, @{"Version" = "6.0."; "Name" = "Windows Server 2008 Member Server"; "ProductType" = "ProductType = `"3`""}, @{"Version" = "6.0."; "Name" = "Windows Server 2008 Domain Controller"; "ProductType" = "ProductType = `"2`""}, @{"Version" = "6.1."; "Name" = "Windows Server 2008 R2"; "ProductType" = "(ProductType = `"3`" OR ProductType = `"2`")" }, @{"Version" = "6.1."; "Name" = "Windows Server 2008 R2 Member Server"; "ProductType" = "ProductType = `"3`""}, @{"Version" = "6.1."; "Name" = "Windows Server 2008 R2 Domain Controller"; "ProductType" = "ProductType = `"2`""}, @{"Version" = "6.2."; "Name" = "Windows Server 2012"; "ProductType" = "(ProductType = `"3`" OR ProductType = `"2`")" }, @{"Version" = "6.2."; "Name" = "Windows Server 2012 Member Server"; "ProductType" = "ProductType = `"3`""}, @{"Version" = "6.2."; "Name" = "Windows Server 2012 Domain Controller"; "ProductType" = "ProductType = `"2`""}, @{"Version" = "6.3."; "Name" = "Windows Server 2012 R2"; "ProductType" = "(ProductType = `"3`" OR ProductType = `"2`")" }, @{"Version" = "6.3."; "Name" = "Windows Server 2012 R2 Member Server"; "ProductType" = "ProductType = `"3`""}, @{"Version" = "6.3."; "Name" = "Windows Server 2012 R2 Domain Controller"; "ProductType" = "ProductType = `"2`""}, @{"Version" = "10.0"; "Name" = "Windows Server 2016"; "ProductType" = "(ProductType = `"3`" OR ProductType = `"2`")" }, @{"Version" = "10.0"; "Name" = "Windows Server 2016 Member Server"; "ProductType" = "ProductType = `"3`""}, @{"Version" = "10.0"; "Name" = "Windows Server 2016 Domain Controller"; "ProductType" = "ProductType = `"2`""} ) foreach ($Item in $Servers) { $Expression = @" SELECT * FROM Win32_OperatingSystem WHERE Version LIKE "$($Item.Version)%" AND $($Item.ProductType) "@ $Server = @{"Expression"=@($Expression);"Description"="$($Item.Name)";"Name"="$($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Server } #endregion #region Microsoft Office $Office = @( @{"Name" = "Access"; "File" = "MSACCESS"}, @{"Name" = "Excel"; "File" = "EXCEL"}, @{"Name" = "Groove"; "File" = "GROOVE"}, @{"Name" = "Infopath"; "File" = "INFOPATH"}, @{"Name" = "Lync"; "File" = "lync"}, @{"Name" = "OneNote"; "File" = "ONENOTE"}, @{"Name" = "Outlook"; "File" = "OUTLOOK"}, @{"Name" = "Powerpoint"; "File" = "POWERPNT"}, @{"Name" = "Project"; "File" = "WINPROJ"}, @{"Name" = "Publisher"; "File" = "MSPUB"}, @{"Name" = "Sharepoint Designer"; "File" = "SPDESIGN"}, @{"Name" = "Visio"; "File" = "VISION"}, @{"Name" = "Word"; "File" = "WINWORD"} ) foreach ($Item in $Office) { $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files (x86)\\Microsoft Office\\Office15\\" AND filename="$($Item.File)" AND extension="exe" AND version>="15" AND version<"16" "@ $Office2013x86 = @{"Expression"=@($Expression);"Description"="Microsoft Office 2013 x86 - $($Item.Name)";"Name"="Microsoft Office 2013 x86 - $($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Office2013x86 $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files\\Microsoft Office\\Office15\\" AND filename="$($Item.File)" AND extension="exe" AND version>="15" AND version<"16" "@ $Office2013x64 = @{"Expression"=@($Expression);"Description"="Microsoft Office 2013 x64 - $($Item.Name)";"Name"="Microsoft Office 2013 x64 - $($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Office2013x64 $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files (x86)\\Microsoft Office\\Office16\\" AND filename="$($Item.File)" AND extension="exe" AND version>="16" AND version<"17" "@ $Office2016x86 = @{"Expression"=@($Expression);"Description"="Microsoft Office 2016 x86 - $($Item.Name)";"Name"="Microsoft Office 2016 x86 - $($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Office2016x86 $Expression = @" SELECT path,filename,extension,version FROM CIM_DataFile where path="\\Program Files\\Microsoft Office\\Office16\\" AND filename="$($Item.File)" AND extension="exe" AND version>="16" AND version<"17" "@ $Office2016x64 = @{"Expression"=@($Expression);"Description"="Microsoft Office 2016 x64 - $($Item.Name)";"Name"="Microsoft Office 2016 x64 - $($Item.Name)";"Namespace"="ROOT\CIMV2";} $Filters += $Office2016x64 } $Expression = @" SELECT * from Win32_Software where displayname like "Microsoft Office%2013%" "@ $Office2013x86System = @{"Expression"=@($Expression);"Description"="Microsoft Office 2013 x86 - System";"Name"="Microsoft Office 2013 x86 - System";"Namespace"="ROOT\CIMV2";} $Filters += $Office2013x86System $Expression = @" SELECT * from Win32_Software64 where displayname like "Microsoft Office%2013%" "@ $Office2013x64System = @{"Expression"=@($Expression);"Description"="Microsoft Office 2013 x64 - System";"Name"="Microsoft Office 2013 x64 - System";"Namespace"="ROOT\CIMV2";} $Filters += $Office2013x64System $Expression = @" SELECT * from Win32_Software where displayname like "Microsoft Office%2016%" "@ $Office2016x86System = @{"Expression"=@($Expression);"Description"="Microsoft Office 2016 x86 - System";"Name"="Microsoft Office 2016 x86 - System";"Namespace"="ROOT\CIMV2";} $Filters += $Office2016x86System $Expression = @" SELECT * from Win32_Software64 where displayname like "Microsoft Office%2016%" "@ $Office2016x64System = @{"Expression"=@($Expression);"Description"="Microsoft Office 2016 x64 - System";"Name"="Microsoft Office 2016 x64 - System";"Namespace"="ROOT\CIMV2";} $Filters += $Office2016x64System #endregion #region Server Features $Expression = @" SELECT * FROM Win32_OptionalFeature Where (Name = "IIS-WebServerRole" OR Name = "IIS-WebServer") AND InstallState = 1 "@ $IIS = @{"Expression"=@($Expression);"Description"="IIS";"Name"="IIS";"Namespace"="ROOT\CIMV2";} $Filters += $IIS $Expression = @" SELECT * FROM Win32_OptionalFeature WHERE (Name = "DNS-Server-Tools" OR Name = "RSAT-AD-Tools-Feature" OR Name = "RSAT-ADDS-Tools-Feature" OR Name = "DirectoryServices-DomainController-Tools" OR Name = "DirectorySerrvices-AdministrativeCenter") AND InstallState = 1 "@ $ADManagementSystem = @{"Expression"=@($Expression);"Description"="Active Directory Management System - Feature Query";"Name"="Active Directory Management System - Feature Query";"Namespace"="ROOT\CIMV2";} $Filters += $ADManagementSystem $Expression = @" SELECT * FROM Win32_Service WHERE Name = "WMSvc" "@ $WebManagementService=@{"Expression"=@($Expression);"Description"="IIS Web Management Service";"Name"="IIS Web Management Service";"Namespace"="ROOT\CIMV2";} $Filters += $WebManagementService $Expression = @" SELECT * FROM Win32_ComputerSystem where DomainRole = 5 "@ $PDC = @{"Expression"=@($Expression);"Description"="PDC Emulator";"Name"="PDC Emulator";"Namespace"="ROOT\CIMV2";} $Filters += $PDC $Expression = @" SELECT * FROM win32_optionalfeature WHERE Name = "FailoverCluster-FullServer" AND InstallState = 1 "@ $WSFC = @{"Expression"=@($Expression);"Description"="Windows Server Failover Cluster Feature";"Name"="Windows Server Failover Cluster Feature";"Namespace"="ROOT\CIMV2";} $Filters += $WSFC $Expression = @" SELECT * from win32_optionalfeature where name = "CertificateServices" AND installstate = 1 "@ $CA = @{"Expression"=@($Expression);"Description"="Active Directory Certificate Services";"Name"="Active Directory Certificate Services";"Namespace"="ROOT\CIMV2";} $Filters += $CA $Expression = @" SELECT * from win32_optionalfeature WHERE (name = "WebEnrollmentServices" AND installstate = 1) "@ $CAWebEnrollment = @{"Expression"=@($Expression);"Description"="Active Directory Certificate Services - CA Web Enrollment";"Name"="Active Directory Certificate Services - CA Web Enrollment";"Namespace"="ROOT\CIMV2";} $Filters += $CAWebEnrollment #endregion #region Software $Expression = @" select * from win32_software64 where DisplayName like "%Java%7%" AND VersionMajor = 7 "@ $JRE7x64 = @{"Expression"=@($Expression);"Description"="JRE 7 x64";"Name"="JRE 7 x64";"Namespace"="ROOT\CIMV2";} $Filters += $JRE7x64 $Expression = @" select * from win32_software where DisplayName like "%Java%7%" AND VersionMajor = 7 "@ $JRE7x86 = @{"Expression"=@($Expression);"Description"="JRE 7 x86";"Name"="JRE 7 x86";"Namespace"="ROOT\CIMV2";} $Filters += $JRE7x86 $Expression = @" select * from win32_software64 where DisplayName like "%Java%8%" AND VersionMajor = 8 "@ $JRE8x64 = @{"Expression"=@($Expression);"Description"="JRE 8 x64";"Name"="JRE 8 x64";"Namespace"="ROOT\CIMV2";} $Filters += $JRE8x64 $Expression = @" select * from win32_software where DisplayName like "%Java%8%" AND VersionMajor = 8 "@ $JRE8x86 = @{"Expression"=@($Expression);"Description"="JRE 8 x86";"Name"="JRE 8 x86";"Namespace"="ROOT\CIMV2";} $Filters += $JRE8x86 #endregion #endregion foreach ($Domain in $Domains) { foreach ($Filter in $Filters) { Write-Verbose -Message "Adding filter $($Filter.Name)" $Filters += New-GPOWmiFilter -Name $Filter.Name -Description $Filter.Description -Expression $Filter.Expression -Domain $Domain -Namespace $Filter.Namespace -PassThru -WithReplace:$WithReplace -Credential $Credential } } } End { if ($PassThru) { Write-Output $Filters } } } Function New-GPOWmiFilter { <# .SYNOPSIS Creates a new WMI filter. .DESCRIPTION The cmdlet takes in the WMI filter properties and produces a usable WMI filter. .PARAMETER Name The WMI filter name. .PARAMETER Expression The expressions to evaluate in the filter, can be multiple WMI queries for a single filter. .PARAMETER Namespace A dynamically generated parameter of the available WMI namespaces. Defaults to ROOT\CIMV2. .PARAMETER DESCRIPTION The WMI filter description. This defaults to the WMI Filter Name. .PARAMETER Domain The domain the WMI filter is being created in, defaults to the domain of the local computer. .PARAMETER Author The author creating the WMI filter, defaults to the current user. .PARAMETER Credential The credential to use to create the filter. .PARAMETER WithReplace If a WMI filter exists with the same name, the WMI query expression is updated. .PARAMETER PassThru Determines whether to return the new WMI filter object. .EXAMPLE New-GPOWmiFilter -Name "Windows Server 2012 R2" -Expression @("Select * From Win32_OperatingSystem Where Version like '6.3%' and ProductType != 1") Creates a WMI filter to target Windows Server 2012 R2. .INPUTS System.String, System.String[], System.String, System.String System.Object .OUTPUTS Microsoft.ActiveDirectory.Management.ADObject .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> [CmdletBinding(DefaultParameterSetName="Default")] Param ( [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0,ParameterSetName="Default")] [string]$Name, [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=1,ParameterSetName="Default")] [string[]]$Expression, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=3,ParameterSetName="Default")] [string]$Description = [System.String]::Empty, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=4,ParameterSetName="Default")] [string]$Domain = [System.String]::Empty, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true,Position=5,ParameterSetName="Default")] [string]$Author = [System.String]::Empty, [Parameter(ValueFromPipelineByPropertyName=$true,ParameterSetName="Default")] [switch]$WithReplace, [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,ParameterSetName="InputObject")] [object]$InputObject, [Parameter(Mandatory=$false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory=$false)] [System.Management.Automation.SwitchParameter]$PassThru = $false ) DynamicParam { # Set the dynamic parameters' name $ParameterName = "Namespace" # Create the dictionary $RuntimeParameterDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary # Create the collection of attributes $AttributeCollection = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute] # Create and set the parameters' attributes $ParameterAttribute = New-Object -TypeName System.Management.Automation.ParameterAttribute $ParameterAttribute.Mandatory = $false $ParameterAttribute.Position = 2 $ParameterAttribute.ParameterSetName ="Default" # Add the attributes to the attributes collection $AttributeCollection.Add($ParameterAttribute) # Generate and set the ValidateSet $ArraySet = Get-WMIObject -Class __Namespace -Namespace "ROOT" | Select-Object -ExpandProperty Name | ForEach-Object {return "ROOT\" + $_} $ValidateSetAttribute = New-Object -TypeName System.Management.Automation.ValidateSetAttribute($ArraySet) # Add the ValidateSet to the attributes collection $AttributeCollection.Add($ValidateSetAttribute) # Create and return the dynamic parameter $RuntimeParameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection) $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter) return $RuntimeParameterDictionary } Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } } Process { if ($PSCmdlet.ParameterSetName -eq "InputObject") { $Author = $InputObject.Author $Expression = $InputObject.Expression $Domain = $InputObject.Domain $Namespace = $InputObject.Namespace $Name = $InputObject.Name $Description = $InputObject.Description } if ($Expression.Count -lt 1) { throw [System.ArgumentException]("At least one Expression Method is required to create a WMI Filter.") } if ([System.String]::IsNullOrEmpty($Domain)) { $ADDomain = Get-ADDomain -Current LocalComputer } else { $ADDomain = Get-ADDomain -Identity $Domain } $DefaultNamingContext = $ADDomain.DistinguishedName $Server = [System.String]::Empty if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { $Server = $env:COMPUTERNAME } } if ([System.String]::IsNullOrEmpty($Server)) { $ContextType = [System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain $Context = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext($ContextType, $ADDomain.DnsRoot) $Server = ([System.DirectoryServices.ActiveDirectory.DomainController]::FindOne($Context)).Name } if ([System.String]::IsNullOrEmpty($PSBoundParameters.Namespace)) { $Namespace = "ROOT\CIMv2" } else { $Namespace = $PSBoundParameters.Namespace } if (![System.String]::IsNullOrEmpty($Author)) { $msWMIAuthor = $Author } else { $msWMIAuthor = Get-Author } if ([System.String]::IsNullOrEmpty($Description)) { $Description = $Name } $msWMIParm2 = $Expression.Count.ToString() + ";" foreach($Exp in $Expression) { $msWMIParm2 += "3;10;$($Exp.Length);WQL;$Namespace;$Exp;" } $msWMICreationDate = (Get-Date).ToUniversalTime().ToString("yyyyMMddhhmmss.ffffff-000") $msWMIChangeDate = $msWMICreationDate $Replaced = $false if ($WithReplace) { $Filter = Get-ADObject -Filter {(objectClass -eq "msWMI-SOM") -and (msWMI-Name -eq $Name)} -Server $Server if ($Filter -ne $null) { $Filter = $Filter | Select-Object -First 1 if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $ADObject = Set-ADObject -Identity $Filter.DistinguishedName -Replace @{"msWMI-Parm2"=$msWMIParm2;"msWMI-ChangeDate"=$msWMIChangeDate} -Server $Server -Credential $Credential -PassThru $Replaced = $true } else { $ADObject = Set-ADObject -Identity $Filter.DistinguishedName -Replace @{"msWMI-Parm2"=$msWMIParm2;"msWMI-ChangeDate"=$msWMIChangeDate} -Server $Server -PassThru $Replaced = $true } } } if ($Replaced -eq $false) { $WMIGUID = "{" + ([System.Guid]::NewGuid().ToString()) + "}" $msWMIParm1 = $Description $Attr = @{ "msWMI-Name" = $Name; "msWMI-Parm1" = $msWMIParm1; "msWMI-Parm2" = $msWMIParm2; "msWMI-Author" = $msWMIAuthor; "msWMI-ID"= $WMIGUID; "instanceType" = 4; "showInAdvancedViewOnly" = "TRUE"; "msWMI-ChangeDate" = $msWMICreationDate; "msWMI-CreationDate" = $msWMICreationDate } $WMIPath = "CN=SOM,CN=WMIPolicy,CN=System,$DefaultNamingContext" if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $ADObject = New-ADObject -Name $WMIGUID -Type "msWMI-Som" -Server $Server -Path $WMIPath -OtherAttributes $Attr -Credential $Credential -PassThru } else { $ADObject = New-ADObject -Name $WMIGUID -Type "msWMI-Som" -Server $Server -Path $WMIPath -OtherAttributes $Attr -PassThru } } } End { if ($PassThru) { Write-Output $ADObject } } } Function Get-Author { <# .SYNOPSIS Gets the user identity to use as an author attribute. .DESCRIPTION Uses the current user environment variable to check active directory to get the user name. .EXAMPLE Get-Author Returns the current user's UserPrincipalName if available, otherwise their SamAccountName or logged in user name. .INPUTS None .OUTPUTS System.String .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/27/2016 #> [CmdletBinding()] Param( ) Begin { $UseAD = $true Import-Module ActiveDirectory -ErrorAction Stop [string]$Author = [System.String]::Empty } Process { try { if ($UseAD) { $User = Get-ADUser $env:USERNAME -ErrorAction Stop if (![System.String]::IsNullOrEmpty($User.UserPrincipalName)) { $Author = $User.UserPrincipalName } else { $Author = $User.SamAccountName } } } catch [Exception] { $Author = ("$env:COMPUTERNAME\" + [System.Security.Principal.WindowsIdentity]::GetCurrent().Name) } } End { Write-Output $Author } } Function Get-OldUsers { <# .SYNOPSIS Get users that haven't logged in for the specified time. .DESCRIPTION The cmdlet finds all users that haven't logged in for the specified time. It first looks for the msDS-LastSuccessfulInteractiveLogon if that has been enabled in the domain. If it is not present, it uses the LastLogonTimeStamp. .PARAMETER OlderThan The number of days since the last logon. .EXAMPLE Get-OldUsers -DaysOld 60 Gets all users that haven't logged on in at least 60 days. .EXAMPLE Get-OldUsers -DaysOld 60 -SearchBase "OU=Finance,OU=HQ,DC=contoso,DC=com" Gets all users in the Finance OU and child OUs that haven't logged on in at least 60 days. .INPUTS System.Int32, System.String .OUTPUTS System.Management.Automation.PSObject[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> [CmdletBinding()] Param( [Parameter(Position=0,Mandatory=$true,ValueFromPipeLineByPropertyName=$true,ValueFromPipeLine=$true)] [int]$DaysOld, [Parameter(Position=1,ValueFromPipeLineByPropertyName=$true)] [string]$SearchBase = [System.String]::Empty ) Begin { $Result = @() if ($SearchBase -eq [System.String]::Empty) { $SearchBase = (Get-ADDomain -Current LoggedOnUser).DistinguishedName } $Users = Get-ADUser -Filter * -Property UserAccountControl,"msDS-LastSuccessfulInteractiveLogonTime",LastLogonTimeStamp,LastLogon -SearchBase $SearchBase | Select-Object -Property DistinguishedName,Enabled,LastLogon,LastLogonTimeStamp,"msDS-LastSuccessfulInteractiveLogonTime",ObjectGUID,SID,UserAccountControl } Process { foreach ($User in $Users) { $UserObject = New-Object -TypeName PSObject -Property @{"Days Since Last Logon"= 0; UserName = $User.SamAccountName; "Last Logon" = $null; UAC=$User.UserAccountControl} if ($User."msDS-LastSuccessfulInteractiveLogonTime" -ne $null -and $User."msDS-LastSuccessfulInteractiveLogonTime" -ne [System.String]::Empty -and $User."msDS-LastSuccessfulInteractiveLogonTime" -ne 0) { [DateTime]$UserObject."Last Logon" = (Get-Date -Date $User."msDS-LastSuccessfulInteractiveLogonTime").AddYears(1600) } else { [DateTime]$UserObject."Last Logon" = (Get-Date -Date $User.LastLogonTimeStamp) } $UserObject."Day Since Last Logon" = ((Get-Date) - $User."Last Logon").TotalDays if ($UserObject."Day Since Last Logon" -ge $DaysOld) { $Result += $UserObject } } } End { Write-Output $Result } } Function Set-PDCEmulatorSrvRecords { <# .SYNOPSIS Changes the PDC Emulator Srv DNS Records for every domain in the forest. .DESCRIPTION The cmdlet defaults to reducing the priority and increasing the weight of the PDC Emulator in every domain so that it does not process logons. .PARAMETER Priority The priority to set on the DNS Srv Records for each PDC. .PARAMETER Weight The weight to set on the DNS Srv Records for each PDC. .PARAMETER RestartServer Indicate whether to restart the server in order to complete the change. .PARAMETER Credential The credentials used to connect to the PDC Emulator, the user must have Enterprise Admin privileges. .EXAMPLE Set-PDCEmulatorSrvRecords Sets the priority to 200 and weight to 25. .EXAMPLE Set-PDCEmulatorSrvRecords -Priority 150 -Weight 20 Sets the priority to 150 and weight to 20. .EXAMPLE Set-PDCEmulatorSrvRecords -RestartServer Sets the priority to 200 and weight to 25 and restarts the server immediately to implement the change. .INPUTS System.Int32, System.Int32, System.Boolean, System.Management.Automation.PSCredential .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> Param ( [Parameter(Position = 0,ValueFromPipeLineByPropertyName=$true)] [int]$Priority = 200, [Parameter(Position = 1,ValueFromPipeLineByPropertyName=$true)] [int]$Weight = 25, [Parameter(Position = 2,ValueFromPipeLineByPropertyName=$true)] [bool]$RestartServer = $false, [Parameter(Position = 3,ValueFromPipeLineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter()] [switch]$PassThru ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } } Process { $Forest = Get-ADForest -Current LoggedOnUser $Domains = $Forest.Domains foreach ($Domain in $Domains) { $PDCEmulator = Get-ADDomain -Identity $Domain | Select-Object -ExpandProperty PDCEmulator $Result = Invoke-Command -ComputerName $PDCEmulator -ScriptBlock { Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name "LdapSrvPriority" -Value $args[0] Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name "LdapSrvWeight" -Value $args[1] $SrvPriority = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name "LdapSrvPriority" | Select-Object -ExpandProperty LdapSrvPriority $SrvWeight = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters -Name "LdapSrvWeight" | Select-Object -ExpandProperty LdapSrvWeight return New-Object -TypeName PSCustomObject -Property @{"LdapSrvPriority" = $SrvPriority; "LdapSrvWeight" = $SrvWeight} } -ArgumentList $Priority,$Weight -Credential $Credential } } End { if ($RestartServer) { Write-Host "Restarting " + $PDCEmulator + " now." Restart-Computer -ComputerName $PDCEmulator -Protocol WSMan -Impersonation Impersonate -Wait $false } else { Write-Warning "The change will not take effect until the $PDCEmulator is restarted." } if ($PassThru) { Write-Output (New-Object -TypeName PSCustomObject -Property @{"Domain" = $Domain; "PDC Emulator" = $PDCEmulator; "LdapSrvPriority" = $Result.LdapSrvPriority; "LdapSrvWeight" = $Result.LdapSrvWeight}) } } } Function Start-SDProp { <# .SYNOPSIS Runs the SDProp process immediately. .DESCRIPTION The function sets the runProtectAdminGroupsTask on the RootDSE object to 1 and runs SDProp. .EXAMPLE Start-SDProp Launches the SDProp process. .INPUTS None .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> [CmdletBinding()] Param() Begin {} Process{ $RootDSE = [ADSI]"LDAP://RootDSE" $RootDSE.Put("runProtectAdminGroupsTask",1) $RootDSE.SetInfo() } End {} } Function Set-AdminSDHolderPermissions { <# .SYNOPSIS Enabling the NETWORK SERVICE account to write SPNs to the AdminSDHolder object prevents error 10154 .DESCRIPTION The Set-AdminSDHolderPermissions cmdlet allows the NETWORK SERVICE account to have "Validated Write to Service Principal Name". The error this is received will look like the following if this command needs to be run: The WinRM service failed to create the following SPNs: WSMAN/servername.fqdn; WSMAN/servername Additional Data The error received was 1355: %%1355. User Action The SPN can be created by an administrator using setspn.exe utility. A background process, Security Descriptor Propagator Update (SDProp), on the PDC emulator runs every 60 minutes and compares the ACL of the AdminSDHolder object with the ACL of the protected users, group and computers. If there are any differences it overwrites them. .EXAMPLE Set-AdminSDHolderPermissions Configures the permissions on the object. .INPUTS None .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> [CmdletBinding()] Param() Begin{} Process { if (Test-IsEnterpriseAdmin) { $Domains = Get-ForestDomains foreach ($Domain in $Domains) { $DomainSID = Get-ADDomain $Domain | Select-Object -ExpandProperty DomainSID [System.DirectoryServices.ActiveDirectoryAccessRule[]] $Rules = New-NetworkServiceAccessRuleSet -DomainSID $DomainSID Set-Permissions -Domain $Domain -Rules $Rules -ObjectCN "CN=AdminSDHolder,CN=System" } } else { Write-Error "Current user is not an Enterprise Admin, run the command again with Enterprise Admin credentials." throw [System.UnauthorizedAccessException]("Current user is not an Enterprise Admin, run the command again with Enterprise Admin credentials.") } } End {} } Function New-NetworkServiceAccessRuleSet { <# .SYNOPSIS Creates the Active Directory Access Rule for the Network Service Account to modify the AdminSDHolder object. .DESCRIPTION Creates the Active Directory Access Rule for the Network Service Account to modify the AdminSDHolder object. .EXAMPLE New-NetworkServiceAccessRuleSet Creates the rule set for the domain of the current user. .PARAMETER DomainSID The Domain SID of the Domain being configured .INPUTS System.Security.Principal.SecurityIdentifier .OUTPUTS System.DirectoryServices.ActiveDirectoryAccessRule[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 3/18/2016 #> Param ( [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true)] [System.Security.Principal.SecurityIdentifier]$DomainSID = $null ) Begin { $Forest = Get-ADForest -Current LoggedOnUser $ForestDN = (Get-ADDomain -Identity ($Forest.RootDomain)).DistinguishedName if ($DomainSID -eq $null) { $DomainSID = (Get-ADDomain -Current LoggedOnUser).DomainSID } } Process { #f3a64788-5306-11d1-a9c5-0000f80367c1 $ExtendedRightGuid = Get-ADObject -Identity ("CN=Validated-SPN,CN=Extended-Rights,CN=Configuration," + $ForestDN) -Properties RightsGUID | Select-Object -ExpandProperty RightsGUID $NetworkService = New-Object Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::NetworkServiceSid, $DomainSID) $Ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($NetworkService, [System.DirectoryServices.ActiveDirectoryRights]::Self, [System.Security.AccessControl.AccessControlType]::Allow, $ExtendedRightGuid, [System.DirectoryServices.ActiveDirectorySecurityInheritance]::None) [System.DirectoryServices.ActiveDirectoryAccessRule[]] $Rules = @($Ace) } End { Write-Output $Rules } } Function Set-Permissions { <# .SYNOPSIS Sets permissions on an Active Directory object. .DESCRIPTION Will set permissions on an Active Directory object with the provided rule set and Object CN. .EXAMPLE Set-Permissions -Domain contoso.com -ObjectCN "CN=AdminSDHolder,CN=System" -Rules $Rules Sets permissions on the AdminSDHolder container in the contoso.com domain with the ACL rules provided. .PARAMETER Domain The domain in which to configure the object's ACL. .PARAMETER ObjectCN The CN of the object being configured up to the domain part of the DN. This can be an empty string to configure the domain object. .PARAMETER Rules An array of Active Directory Access Rules .INPUTS System.String, System.String, System.DirectoryServices.ActiveDirectoryAccessRule[] .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true,Position=0,ValueFromPipeLineByPropertyName=$true)] [string]$Domain, [Parameter(Mandatory=$true,Position=1,ValueFromPipeLineByPropertyName=$true)] [AllowEmptyString()] [String]$ObjectCN, [Parameter(Mandatory=$true,Position=2,ValueFromPipeLineByPropertyName=$true)] [System.DirectoryServices.ActiveDirectoryAccessRule[]]$Rules ) Begin { $DN = (Get-ADDomain -Identity $Domain).DistinguishedName $DC = Get-ADDomainController -DomainName $Domain -Discover [String[]]$Drives = Get-PSDrive | Select-Object -ExpandProperty Name } Process { if ($DC -ne $null) { if (Test-Connection -ComputerName $DC) { $TempDrive = "tempdrive" if ($Drives.Contains($TempDrive)) { Write-Host "An existing PSDrive exists with name $TempDrive, temporarily removing" -ForegroundColor Yellow $OldDrive = Get-PSDrive -Name $TempDrive Remove-PSDrive -Name $TempDrive } $Drive = New-PSDrive -Name $TempDrive -Root "" -PSProvider ActiveDirectory -Server $DC.Name Push-Location -Path "$Drive`:\" if ($ObjectCN -eq "") { $ObjectDN = $DN } else { $ObjectDN = $ObjectCN + "," + $DN } [string[]]$Paths = @() $ObjectToChange = Get-ADObject -Identity $ObjectDN -Server $DC $Paths += $Drive.Name + ":" + $ObjectToChange.DistinguishedName $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext( [System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain, $Domain) [System.DirectoryServices.ActiveDirectory.DomainControllerCollection]$DomainControllers = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context) foreach ($DomainController in $DomainControllers) { $Paths += (Get-ADDomainController -Identity $DomainController.Name -Server $DC | Select-Object -ExpandProperty ComputerObjectDN) } foreach ($Path in $Paths) { try { $Acl = Get-Acl -Path $Path if ($Acl) { $OldAcls = $Acl.Access | Where-Object {$Rules.IdentityReference -eq $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier])} foreach ($Rule in $OldAcls) { $Acl.RemoveAccessRule($Rule) | Out-Null } foreach ($Rule in $Rules) { $Acl.AddAccessRule($Rule) | Out-Null } Set-Acl -Path $Path -AclObject $Acl Write-Results -Path $Path -Domain $Domain } else { Write-Warning "Could not retrieve the ACL for $Path" } } catch [System.Exception] { Write-Warning $_.ToString() } } Pop-Location Remove-PSDrive $Drive if ($OldDrive) { Write-Host "Recreating original PSDrive" -ForegroundColor Yellow New-PSDrive -Name $OldDrive.Name -PSProvider $OldDrive.Provider -Root $OldDrive.Root | Out-Null Remove-Variable -Name "OldDrive" } } else { Write-Warning "Could not contact domain controller $DC" } } } End {} } function Write-Results { <# .SYNOPSIS Writes the ACL configuration output results. .DESCRIPTION The Write-Results cmdlet OUTPUTS the modified ACL. .EXAMPLE Write-Results -Path "c:\test.txt" -Domain "contoso.com" Writes the current ACL of test.txt .INPUTS System.String, System.String .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 12/5/2015 #> [CmdletBinding()] Param ( [Parameter(Position=0,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [String]$Path, [Parameter(Position=1,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [String]$Domain ) Begin{} Process { try { $Acl = Get-Acl -Path $Path } catch [Exception] { Write-Warning $_.Exception.Message throw $_.Exception } } End { Write-Host $Domain -ForegroundColor DarkRed -BackgroundColor White Write-Host ($Path.Substring($Path.IndexOf(":") + 1)) -ForegroundColor DarkRed -BackgroundColor White Write-Host ($Acl.Access | Format-List | Out-String) } } Function Test-IsEnterpriseAdmin { <# .SYNOPSIS Tests if a user is a member of the Enterprise Admins group. .DESCRIPTION The Test-IsEnterpriseAdmin returns true if the user is in the group and false otherwise. .EXAMPLE Test-IsEnterpriseAdmin Determines if the user credentials being used to run the cmdlet have Enterprise Admin privileges. .EXAMPLE Test-IsEnterpriseAdmin -UserName "John Smith" Determines if the user John Smith has Enterprise Admin privileges. .EXAMPLE Test-IsEnterpriseAdmin -Credential (Get-Credential) Determines if the entered user credentials have Enterprise Admin privileges. .PARAMETER UserName The user to test the group membership on. If no user name is specified, the cmdlet runs against WindowsIdentity Principal. .PARAMETER Credential The PSCredential to use to test if the user has Enterprise Admin credentials. .INPUTS System.Management.Automation.PSCredential, System.String System.String, System.String .OUTPUTS System.Boolean .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/24/2016 #> [CmdletBinding(DefaultParameterSetName="Username")] Param ( [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Username")] [string]$UserName = [System.String]::Empty, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Credential")] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } [bool]$IsAdmin = $false } Process { switch ($PSCmdlet.ParameterSetName) { "Username" { if ($UserName -ne [System.String]::Empty) { $CurrentUser = $UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { Write-Host "Current principal is the SYSTEM account." $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } "Credential" { if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $CurrentUser = $Credential.UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { Write-Host "Current principal is the SYSTEM account." $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } default { throw "Could not determine parameter set name for Test-IsEnterpriseAdmin." } } if(!$IsAdmin) { if ($CurrentUser.IndexOf("\") -ne -1) { $Domain = $CurrentUser.Substring(0, $CurrentUser.IndexOf("\")) $Forest = (Get-ADDomain -Identity $Domain).Forest $CurrentUser = $CurrentUser.Substring($CurrentUser.IndexOf("\") + 1) } elseif ($CurrentUser.IndexOf("@") -ne -1) { $Domain = $CurrentUser.Substring($CurrentUser.IndexOf("@") + 1) $Forest = (Get-ADDomain -Identity $Domain).Forest $CurrentUser = $CurrentUser.Substring(0, $CurrentUser.IndexOf("@")) } else { $Forest = (Get-ADForest -Current LoggedOnUser).Name } $Groups = Get-NestedGroupMembership -Principal $CurrentUser | Select-Object -Property Name,SID if($Credential -ne $null -and $Credential -ne [System.Management.Automation.PSCredential]::Empty) { $RootDomainSID = Get-ADDomain -Identity $Forest -Credential $Credential | Select-Object -ExpandProperty DomainSID } else { $RootDomainSID = Get-ADDomain -Identity $Forest | Select-Object -ExpandProperty DomainSID } [Security.Principal.SecurityIdentifier]$EnterpriseAdminSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid, $RootDomainSID) foreach ($Group in $Groups) { if ($Group.SID -eq $EnterpriseAdminSID) { $IsAdmin = $true break } } } } End { Write-Output $IsAdmin } } Function Test-IsEnterpriseOrDomainAdmin { <# .SYNOPSIS Tests whether the current user or provided credential is a Domain Admin or Enterprise Admin. .DESCRIPTION The Test-IsEnterpriseOrDomainAdmin returns true if the user is in the group and false otherwise. .EXAMPLE Test-IsEnterpriseOrDomainAdmin Determines if the user credentials being used to run the cmdlet have Domain or Enterprise Admin privileges .EXAMPLE Test-IsEnterpriseOrDomainAdmin -Credential $Creds Determines if the credentials have Enterprise or Domain Admin privileges .PARAMETER UserName The user to test the group membership on. If no user name is specified, the cmdlet runs against WindowsIdentity Principal. .PARAMETER Credential The PSCredential to use to test if the user has Enterprise Admin credentials. .INPUTS System.Management.Automation.PSCredential, System.String System.String, System.String .OUTPUTS System.Boolean .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> [CmdletBinding(DefaultParameterSetName="Username")] Param ( [Parameter(Position=1,ValueFromPipeLineByPropertyName=$true)] [string]$Domain, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Credential")] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName="Username")] [string]$Username = [System.String]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } [bool]$IsAdmin = $false } Process { switch ($PSCmdlet.ParameterSetName) { "Username" { if ($UserName -ne [System.String]::Empty) { $CurrentUser = $UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } break } "Credential" { if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $CurrentUser = $Credential.UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } break } default { throw "Could not determine parameter set name for Test-IsDomainAdmin." } } if (!$IsAdmin) { try { if ($CurrentUser.IndexOf("\") -ne -1) { $Domain = $CurrentUser.Substring(0, $CurrentUser.IndexOf("\")) $CurrentUser = $CurrentUser.Substring($CurrentUser.IndexOf("\") + 1) } elseif ($CurrentUser.IndexOf("@") -ne -1) { $Domain = $CurrentUser.Substring($CurrentUser.IndexOf("@") + 1) $CurrentUser = $CurrentUser.Substring(0, $CurrentUser.IndexOf("@")) } else { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } $CurrentUser = $CurrentUser.Substring($CurrentUser.IndexOf("\") + 1) $Groups = Get-NestedGroupMembership -Principal $CurrentUser | Select-Object -Property Name,SID if ($Credential -ne $null -and $Credential -ne [System.Management.Automation.PSCredential]::Empty) { [Microsoft.ActiveDirectory.Management.ADPartition]$DomainPartition = Get-ADDomain -Identity $Domain -Credential $Credential $RootDomainSID = Get-ADDomain -Identity ($DomainPartition.Forest) -Credential $Credential | Select-Object -ExpandProperty DomainSID } else { [Microsoft.ActiveDirectory.Management.ADPartition]$DomainPartition = Get-ADDomain -Identity $Domain $RootDomainSID = Get-ADDomain -Identity ($DomainPartition.Forest) | Select-Object -ExpandProperty DomainSID } $DomainSID = $DomainPartition.DomainSID [Security.Principal.SecurityIdentifier]$EnterpriseAdminSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid, $RootDomainSID) [Security.Principal.SecurityIdentifier]$DomainAdminSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::AccountDomainAdminsSid, $DomainSID) foreach ($Group in $Groups) { if ($Group.SID -eq $EnterpriseAdminSID -or $Group.SID -eq $DomainAdminSID) { $IsAdmin = $true break } } } catch [Exception] { $IsAdmin = $false } } } End { Write-Output $IsAdmin } } Function Test-IsDomainAdmin { <# .SYNOPSIS Tests if a user is a member of the Domain Admins group. .DESCRIPTION The Test-IsDomainAdmin returns true if the user is in the group and false otherwise. .EXAMPLE Test-IsDomainAdmin Determines if the user credentials being used to run the cmdlet have Enterprise Admin privileges. .EXAMPLE Test-IsDomainAdmin -UserName "John Smith" Determines if the user John Smith has Domain Admin privileges .PARAMETER UserName The user to test the group membership on. If no user name is specified, the cmdlet runs against WindowsIdentity Principal. .PARAMETER Credential The PSCredential to use to test if the user has Enterprise Admin credentials. .INPUTS System.Management.Automation.PSCredential, System.String System.String, System.String .OUTPUTS System.Boolean .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> [CmdletBinding(DefaultParameterSetName="Username")] Param ( [Parameter(Position=1,ValueFromPipeLineByPropertyName=$true)] [string]$Domain, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Username")] [string]$UserName = [System.String]::Empty, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Credential")] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } [bool]$IsAdmin = $false } Process { switch ($PSCmdlet.ParameterSetName) { "Username" { if ($UserName -ne [System.String]::Empty) { $CurrentUser = $UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } "Credential" { if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $CurrentUser = $Credential.UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } default { throw "Could not determine parameter set name for Test-IsDomainAdmin." } } if (!$IsAdmin) { if ($CurrentUser.IndexOf("\") -ne -1) { $Domain = $CurrentUser.Substring(0, $CurrentUser.IndexOf("\")) $CurrentUser = $CurrentUser.Substring($CurrentUser.IndexOf("\") + 1) } elseif ($CurrentUser.IndexOf("@") -ne -1) { $Domain = $CurrentUser.Substring($CurrentUser.IndexOf("@") + 1) $CurrentUser = $CurrentUser.Substring(0, $CurrentUser.IndexOf("@")) } else { $Domain = (Get-ADDomain -Current LoggedOnUser).DnsRoot } $Groups = Get-NestedGroupMembership -Principal $CurrentUser | Select-Object -Property Name,SID if($Credential -ne $null -and $Credential -ne [System.Management.Automation.PSCredential]::Empty) { $DomainSID = Get-ADDomain -Identity $Domain -Credential $Credential | Select-Object -ExpandProperty DomainSID } else { $DomainSID = Get-ADDomain -Identity $Domain | Select-Object -ExpandProperty DomainSID } [Security.Principal.SecurityIdentifier]$DomainAdminSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::AccountDomainAdminsSid, $DomainSID) foreach ($Group in $Groups) { if ($Group.SID -eq $DomainAdminSID) { $IsAdmin = $true break } } } } End { Write-Output $IsAdmin } } Function Get-ADAccountControl { <# .SYNOPSIS Gets an enumeration of a user's UserAccountControl attribute. .DESCRIPTION The Get-ADAccountControl cmdlet gets the value from a user's UserAccountControl attribute and enumerates it to the different parts that create the bitwise value. .EXAMPLE Get-ADAccountControl -Identity "John Smith" Gets the UAC enumeration for John Smith. .PARAMETER Identity The identity of the user, can be a SamAccountName, DistinguishedName, or DisplayName property. .INPUTS System.String .OUTPUTS System.Management.Automation.PSCustomObject[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/24/2016 #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Identity ) Begin { } Process { $UAC = Get-ADObject -Filter {(SamAccountName -eq $Identity) -or (DistinguishedName -eq $Identity) -or (DisplayName -eq $Identity)} -Properties UserAccountControl | Select-Object -ExpandProperty UserAccountControl Write-Host $Identity $Matches = @() foreach ($Value in $script:UACValues) { #Perform bitwise and to compare the current UAC against each possible value and record matches if ($UAC -band [System.Convert]::ToInt64($Value.Key, 16)) { $Matches += $Value Write-Host $Value.Value } } } End { Write-Output $Matches } } Function Get-NestedGroupMembership { <# .SYNOPSIS Recursively gets the group membership of an AD principal. .DESCRIPTION The Get-NestedGroupMembership gets all nested group membership of an AD principal. .EXAMPLE Get-NestedGroupMembership -Principal Administrator Gets all group membership for the Administrator account. .PARAMETER Principal The principal to get group membership for. .INPUTS System.String .OUTPUTS Microsoft.ActiveDirectory.Management.ADGroup[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> Param( [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Position=0)] [string]$Principal, [Parameter(DontShow=$true)] [Microsoft.ActiveDirectory.Management.ADGroup[]]$Groups ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Groups -eq $null) { $Groups = @() } if ([System.String]::IsNullOrEmpty($Principal)) { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } if ($Principal.IndexOf("DC=") -ne -1) { #Pulls the DC=...,DC=...,DC=... info and turns it into an FQDN $Server = $Principal.Substring($Principal.IndexOf("DC=")).Replace("DC=","").Replace(",",".") } else { if ($Principal.IndexOf("\") -ne -1) { $Domain = $Principal.Substring(0, $Principal.IndexOf("\")) $Server = (Get-ADDomain -Identity $Domain).DnsRoot $Principal = $Principal.Substring($Principal.IndexOf("\") + 1) } elseif ($Principal.IndexOf("@") -ne -1) { $Domain = $Principal.Substring($Principal.IndexOf("@") + 1) $Server = (Get-ADDomain -Identity $Domain).DnsRoot $Principal = $Principal.Substring(0, $Principal.IndexOf("@")) } else { $Server = (Get-ADDomain -Current LoggedOnUser).DnsRoot } } if ([System.String]::IsNullOrEmpty($Server)) { throw "Could not find a domain controller." } } Process { #Get the group membership of the evaluated principal $TempGroups = Get-ADPrincipalGroupMembership -Identity $Principal -Server $Server $GroupsToCheck = @() #Iterate through these groups, need to check if the Groups array already contains the group #If it doesn't, add the group since it is a newly discovered nested group, and also add it to the #array of groups to check for further nested group membership #We don't want to check the Groups array for nested membership since a lot of those groups have already been checked foreach($Group in $TempGroups) { if (!$Groups.Contains($Group)) { $Groups += $Group $GroupsToCheck += $Group } } #This array will hold newly discovered nested groups of the groups we need to check $NewGroups = @() #Get the nested group membership of each new group to check foreach ($Group in $GroupsToCheck) { $NewGroups += Get-NestedGroupMembership -Principal $Group.DistinguishedName -Groups $Groups } #After getting the nested groups, check to see if that group may have been added already to Groups #through nested membership in some other group that was already checked foreach ($Group in $NewGroups) { if (!$Groups.Contains($Group)) { $Groups += $Group } } } End { #Return the updated total group membership Write-Output $Groups } } $script:UACValues = @( [PSCustomObject]@{Key="0x00000001";Value="ADS_UF_SCRIPT"}, [PSCustomObject]@{Key="0x00000002";Value="ADS_UF_ACCOUNT_DISABLE"}, [PSCustomObject]@{Key="0x00000008";Value="ADS_UF_HOMEDIR_REQUIRED"}, [PSCustomObject]@{Key="0x00000010";Value="ADS_UF_LOCKOUT"} [PSCustomObject]@{Key="0x00000020";Value="ADS_UF_PASSWD_NOTREQD"}, [PSCustomObject]@{Key="0x00000040";Value="ADS_UF_PASSWD_CANT_CHANGE"}, [PSCustomObject]@{Key="0x00000080";Value="ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED"}, [PSCustomObject]@{Key="0x00000100";Value="ADS_UF_TEMP_DUPLICATE_ACCOUNT"}, [PSCustomObject]@{Key="0x00000200";Value="ADS_UF_NORMAL_ACCOUNT"}, [PSCustomObject]@{Key="0x00000800";Value="ADS_UF_INTERDOMAIN_TRUST_ACCOUNT"}, [PSCustomObject]@{Key="0x00001000";Value="ADS_UF_WORKSTATION_TRUST_ACCOUNT"}, [PSCustomObject]@{Key="0x00002000";Value="ADS_UF_SERVER_TRUST_ACCOUNT"}, [PSCustomObject]@{Key="0x00010000";Value="ADS_UF_DONT_EXPIRE_PASSWD"}, [PSCustomObject]@{Key="0x00020000";Value="ADS_UF_MNS_LOGON_ACCOUNT"}, [PSCustomObject]@{Key="0x00040000";Value="ADS_UF_SMARTCARD_REQUIRED"}, [PSCustomObject]@{Key="0x00080000";Value="ADS_UF_TRUSTED_FOR_DELEGATION"}, [PSCustomObject]@{Key="0x00100000";Value="ADS_UF_NOT_DELEGATED"}, [PSCustomObject]@{Key="0x00200000";Value="ADS_UF_USE_DES_KEY_ONLY"}, [PSCustomObject]@{Key="0x00400000";Value="ADS_UF_DONT_REQUIRE_PREAUTH"}, [PSCustomObject]@{Key="0x00800000";Value="ADS_UF_PASSWORD_EXPIRED"}, [PSCustomObject]@{Key="0x01000000";Value="ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION"}, [PSCustomObject]@{Key="0x02000000";Value="ADS_UF_NO_AUTH_DATA_REQUIRED"}, [PSCustomObject]@{Key="0x04000000";Value="ADS_UF_PARTIAL_SECRETS_ACCOUNT"} ) |