remove-ADM.ps1
Description
@GitHub Copilot - Welcome @BanterBoy, I’m your Copilot and I’m here to help you get things done faster. I can identify issues, explain and even improve code.
You can ask generic questions, but what I’m really good at is helping you with your code. For example:
Generate unit tests for my code Explain the selected code Propose a fix for the bugs in my code If you want to learn more about my capabilities and limitations, check out the Copilot documentation.
I’m powered by AI, so surprises and mistakes are possible. Make sure to verify any generated code or suggestions, and share feedback so that we can learn and improve.
@BanterBoy: - Explain what the script does and why it is useful.
@GitHub Copilot -
Script
################################################################
# Script: Remove-ADMTemplates.ps1
#
# Tom Moser - PFE
# Mark Morowczynski - PFE
# Questions: http://blogs.technet.com/b/askpfeplat/contact.aspx
# Date: 12/8/2011
# Update 1: 3/1/2012
#
# Usage:
# To remove all ADMs from Sysvol, run the script with a domain administrator account
# or account that has been assigned proper permissions to sysvol.
#
# To remove all ADMs:
# .\Remove-ADM.ps1 -backupPath <Directory> -logPath <CSV file path> (-Whatif) (-domainDN) (-NoDateCheck) (-NoCentralStore)
#
# -backupPath: This is where the ADMs will be copied. A subdirectory will be created for each GPT and the ADMs copied there
# -logPath: This is the path where you will write the logfile for the script. It should be a CSV (ie, C:\Temp\Remove-ADMLog.Csv)
# This log is used as the source when restoring ADMs
#
# -WhatIf: This switch is optional. It won't remove any ADMs but will generate the CSV log
#
# -domainDN: This is an optional parameter. If your environment does NOT have AD Web Services (2008 R2 DCs), or AD Management GW
# or RSAT on Win7/2008 R2, you may specify the DN of the domain (ie, dc=corp,dc=microsoft,dc=com) intead of relying on ADWS.
# You can also use it to specify alternate or child domains.
#
# -NoDateCheck: Skips ADM date comparison. *USE AT YOUR OWN RISK* You should only use this if you're sure that you want to remove all ADMs that
# match the name of the ADMXs in the central store or PolicyDefinitions directory.
#
# -NoCentralStore: This switch tells the script to ignore the central store on sysvol and to instead use the local policydefinitions folder.
# The location is C:\windows\policydefinitions. Customers who do not use the central store, but instead utilize a shared terminal
# server for GP management may want to use this flag. Assumes central store is intended if this switch is not used.
#
# To RESTORE removed ADMs to the appropriate folders:
# .\Remove-ADM.ps1 -logPath <path to log> -Restore
#
# -logpath: This is the path to a log generated by a previous run that removed ADM templates
# -Restore: This switch tells the script to restore based on the contentsn of the file specified in the -logfile parameter.
#
################################################################
param([string]$backupPath,
[string]$logPath,
[switch]$WhatIf,
[switch]$restore,
[string]$domainDN = $null,
[switch]$NoDateCheck,
[switch]$NoCentralStore)
$erroractionpreference = "SilentlyContinue"
#Find domain information. First tries to import the AD Module. If that fails, prompt for domain DN
if ($domainDN -eq [string]::Empty) {
Write-Host -ForegroundColor 'green' "Domain DN not specified. Attempting to use the AD module for discovery."
try {
#AD Module magic
Import-Module ActiveDirectory
$DomainInfo = Get-ADDomain
$DomainFQDN = $DomainInfo.dnsroot
$PDCEmulator = $DomainInfo.PDCEmulator
Write-Host -ForegroundColor 'green' "Successfully imported AD Module"
}
catch {
#The domain DN wasn't specified at start, but loading the AD module failed. Prompt user for the DN and proceed.
Write-Host -ForegroundColor 'red' "Encountered an error using the AD module. Verify that RSAT is installed with the AD PowerShell module and that you have ADWS or ADMGW running on your DC."
$continue = Read-Host "Would you like to enter the domain DN manually? Y or N: "
if ($continue.tolower() -eq "y") {
$domainDN = Read-Host "Please enter the domain DN in the proper format (ie, dc=corp,dc=microsoft,dc=com): "
$confirm = Read-Host "Is this correct? Y or N: $($domainDN)"
if ($confirm.tolower() -ne "y") {
Write-Host "Exiting."
exit
}
}
else {
return
}
}
}
if ($domainDN -ne [string]::empty) {
try {
#adsi - gross.
$ad = [adsi]"LDAP://$($domainDN)"
$PDCcn = ($ad.psbase.properties.fsmoroleowner).tostring().split(',')[1]
$PDCEmulator = $PDCcn.split('=')[1]
$DomainFQDN = $domainDN.tolower().replace("dc=", "")
$DomainFQDN = $DomainFQDN.replace(',', '.')
#If the domain DN is specified, append the DNS suffix to the PDC hostname
#This is to address a potential issue where a user is running the script with an account and machine in Domain A against Domain B
#If the same DC name exists in domain A and domain B, DNS resolution will resolve the Domain A machine name without the suffix appended
$PDCEmulator += ".$DomainFQDN"
}
catch {
Write-Host -ForegroundColor 'red' "Error gathering domain information via ADSI. Please double check your DN formatting."
return
}
}
#build paths for the magic.
$sysvol = "\\$($PDCEmulator)\sysvol"
#If NoCentralStore is set, the script defaults to c:\windows\policydefinitions
#Otherwise, it builds the path to sysvol
if ($NoCentralStore.IsPresent -eq $true) {
$PolicyDefinitionPath = "C:\Windows\PolicyDefinitions"
}
else {
$PolicyDefinitionPath = "$($sysvol)\$($DomainFQDN)\Policies\PolicyDefinitions"
}
$GPTPath = "$($sysvol)\$($domainFQDN)\Policies"
$backupTarget = $null
#this converts all of the out-of-box ADM and date stamps to an object.
#use this if you're checking the date stamps and not overriding with the -NoDateCheck switch
#Add your own ADMs by following the template below.
#The purpose of this check is to let the user know that the out of box ADMs may have been modified at some point
$ADMDateStamps = ConvertFrom-Csv `
"ADM,DateStamp
conf.adm,2/22/2003
inetres.adm,2/18/2005
system.adm,2/18/2005
wmplayer.adm,2/18/2005
wuau.adm,2/18/2005
conf.adm,7/17/2004
inetres.adm,7/23/2004
system.adm,7/17/2004
wmplayer.adm,7/17/2004
wuau.adm,7/17/2004
conf.adm,2/21/2003
inetres.adm,2/21/2003
system.adm,2/21/2003
wmplayer.adm,2/21/2003
wuau.adm,2/21/2003
conf.adm,7/21/2001
inetres.adm,6/6/2002
system.adm,8/21/2002
wmplayer.adm,7/21/2001
wuau.adm,4/15/2002
conf.adm,7/21/2001
inetres.adm,7/21/2001
system.adm,7/21/2001
wmplayer.adm,7/21/2001
conf.adm,2/18/2007
inetres.adm,2/18/2007
system.adm,2/18/2007
wmplayer.adm,2/18/2007
wuau.adm,8/6/2009
conf.adm,4/14/2008
inetres.adm,4/14/2008
system.adm,4/14/2008
wmplayer.adm,4/14/2008"
#Get ADM template list from sysvol
function GetADMTemplates {
param([string]$GPTPath)
#Get all files ending in .ADM from sysvol. For some reason, GCI with the Recurse flag and *.ADM also returns ADML and ADMX.
#The where filters it to just .ADM.
$ADMTemplates = Get-ChildItem $GPTPath *.adm -Recurse | Where-Object { $_.extension.length -eq 4 }
return $ADMTemplates
}
#Get ADMX template list from policy definitions location
function GetADMXTemplateHashTable {
param([string]$PolicyDefinitionPath)
#enumerate central store. Store all ADMX template info in hash table for quick lookups
$ADMXTemplates = @{}
$ADMXFiles = Get-ChildItem $PolicyDefinitionPath *.admx
if ($null -eq $ADMXFiles) {
Write-Host -ForegroundColor 'yellow' "No ADMX templates found in the central store."
return
}
if ($null -eq $ADMXFiles.count) {
$ADMXTemplates.Add($ADMXFiles.Name, $ADMXFiles.Fullname)
}
elseif ($ADMXFiles.count -gt 1) {
$ADMXFiles | ForEach-Object { $ADMXTemplates.Add($_.Name, $_.Fullname) }
}
return $ADMXTemplates
}
#This function does all of the work.
function BackupAndRemoveADM {
param([string]$ADMTemplatePath,
[string]$BackupPath,
[string]$BackupTarget)
if ((Test-Path $backupPath) -eq $false) {
New-Item -ItemType Directory $backuppath
}
#build GUID based backup path
#create new directory in specified backup path if it doesn't exist
if ((Test-Path $backupTarget) -eq $false) {
New-Item -type Directory $backupTarget
}
#copy ADM file to backup location
Copy-Item $ADMTemplatePath $backupTarget
#if copy was successfull, remove ADM from ADM folder
if ($? -eq $true) {
Remove-Item $ADMTemplatePath
Write-Host -ForegroundColor 'yellow' "Removed $($ADMTemplatePath)"
}
}
function IsValidPolicyDefinitionPath {
param([string]$PolicyDefinitionPath)
$result = test-path $PolicyDefinitionPath
return $result
}
function CheckADMDate {
param($ADMDateStamps, [string]$ADMName, [string]$date)
$match = $ADMDateStamps | Where-Object { $_.ADM -eq $ADMName -and $_.DateStamp -eq $date }
if ($null -eq $match) {
return $false
}
return $true
}
#If restore flag is specified, read backup file and backup path, then restore files
function RestoreADMFiles {
param([string]$logpath)
$logData = import-csv $logpath
foreach ($entry in $logData) {
if ($entry.BackupLocation -ne "") {
copy-item $entry.BackupLocation -destination $entry.FullPath
if ($?) {
"Restored $($entry.FullPath)"
}
}
}
}
##################
# Script body
##################
#If restore switch is used, read $logpath and restore ADMs
if ($restore) {
RestoreADMFiles -logpath $logPath
return
}
#Verify specified path exists
if ((IsValidPolicyDefinitionPath($PolicyDefinitionPath)) -eq $false) {
Write-Host -ForegroundColor 'red' "Specified policy definition folder is invalid: $($PolicyDefinitionPath)"
return
}
#create log directory if it doesn't exist
$logDirectory = Split-Path $logpath
if ((Test-Path $logDirectory) -eq $false) {
New-Item -ItemType directory $logDirectory
}
#write log file header
set-content $logpath "TemplateName,FullPath,ADMXInCentralStore,BackupLocation,NoMatch"
#Build ADMX Hash Table
$ADMXTemplates = GetADMXTemplateHashTable($PolicyDefinitionPath)
#Get all ADM Templates on Sysvol
$ADMTemplates = GetADMTemplates($GPTPath)
#If we don't find any ADM templates
if ($null -eq $ADMTemplates) {
Write-Host -ForegroundColor 'yellow' "No ADM Templates found."
return
}
foreach ($ADMTemplate in $ADMTemplates) {
$TemplateName = $ADMTemplate.name.split('.')[0]
if ($TemplateName -eq "WUAU") { $TemplateName = "WindowsUpdate" }
if ($TemplateName -eq "wmplayer") { $TemplateName = "WindowsMediaPlayer" }
if ($TemplateName -eq "system") { $TemplateName = "windowsfirewall" }
switch ($ADMXTemplates.Contains("$($TemplateName).admx")) {
$true {
#if the NoDateCheck switch is used, skip date checking.
if ($NoDateCheck -ne $True) {
if ((CheckADMDate -ADMName $ADMTemplate.Name -Date $ADMTemplate.LastWriteTime.toshortdatestring() -ADMDateStamps $ADMDateStamps) -eq $false) {
"$(($ADMTemplate).FullName) does not match any timestamps from Windows 2003 or Windows XP or any service packs. Verify that it has not been modified."
continue
}
}
$backupTarget = $BackupPath + "\" + $ADMTemplate.FullName.split('\')[6]
if ($whatIf -eq $false) {
BackupAndRemoveADM -ADMTemplatePath $ADMTemplate.FullName -BackupPath $BackupPath -BackupTarget $backupTarget
}
#write log
Add-Content $logpath "$(($ADMTemplate).Name),$(($ADMTemplate).FullName),True,$($backupTarget)\$(($ADMTemplate).Name)"
}
$false {
Write-Host "$(($ADMTemplate).Name) doesn't have a matching ADMX templates. Manually investigate and remove if ADM template is no longer required."
Add-Content $logpath "$(($ADMTemplate).Name),$(($ADMTemplate).FullName),False,,True"
}
}
}
Download
Please feel free to copy parts of the script or if you would like to download the entire script, simple click the download button. You can download the complete repository in a zip file by clicking the Download link in the menu bar on the left hand side of the page.
Report Issues
You can report an issue or contribute to this site on GitHub. Simply click the button below and add any relevant notes. I will attempt to respond to all issues as soon as possible.