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"
        }
    }
}

Back to Top


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.

Issue


Back to Top