top of page

PowerShell - Get-GPO Backup and Report

  • Writer: Jon Boyette
    Jon Boyette
  • Jan 25, 2022
  • 14 min read

Save as Get-GPOBacjupAndReport.ps1, creates 2 zips in system 32 with GPO backups and all GPO details

#Requires -Version 3.0

#requires -Module ActiveDirectory

#requires -Module GroupPolicy



<#

.SYNOPSIS

Creates a Backup and Reports for all Group Policies in the current Active Directory domain.

.DESCRIPTION

Creates a Backup and HTML and XML Reports for all Group Policies in the current Active Directory domain.


This Script requires at least PowerShell version 3 but runs best in version 5.


This script requires at least one domain controller running Windows Server 2008 R2.

This script outputs Text, XML and HTML files.

You do NOT have to run this script on a domain controller, and it is best if you didn't.


This script was developed and run from a Windows 10 domain-joined VM.


This script requires Domain Admin rights and an elevated PowerShell session.

To run the script from a workstation, RSAT is required.

Remote Server Administration Tools for Windows 7 with Service Pack 1 (SP1)

http://www.microsoft.com/en-us/download/details.aspx?id=7887

Remote Server Administration Tools for Windows 8

http://www.microsoft.com/en-us/download/details.aspx?id=28972

Remote Server Administration Tools for Windows 8.1

http://www.microsoft.com/en-us/download/details.aspx?id=39296

Remote Server Administration Tools for Windows 10

http://www.microsoft.com/en-us/download/details.aspx?id=45520

.PARAMETER ADDomain

Specifies an Active Directory domain object by providing one of the following

property values. The identifier in parentheses is the LDAP display name for the

attribute. All values are for the domainDNS object that represents the domain.


Distinguished Name


Example: DC=tullahoma,DC=corp,DC=labaddomain,DC=com


GUID (objectGUID)


Example: b9fa5fbd-4334-4a98-85f1-3a3a44069fc6


Security Identifier (objectSid)


Example: S-1-5-21-3643273344-1505409314-3732760578


DNS domain name


Example: tullahoma.corp.labaddomain.com


NetBIOS domain name


Example: Tullahoma


Default value is $Env:USERDNSDOMAIN


If both ADDomain and OrganizationalUnit are used, the latter takes preference.

.PARAMETER ComputerName

Specifies which domain controller to use to run the script against.

ComputerName can be entered as the NetBIOS name, FQDN, localhost or IP Address.

If entered as localhost, the actual computer name is determined and used.

If entered as an IP address, an attempt is made to determine and use the actual

computer name.

This parameter has an alias of ServerName.

Default value is $Env:USERDNSDOMAIN

.PARAMETER Folder

Specifies the optional output folder to save the output report.

The folder specified must already exist.

.PARAMETER GPOFilter

Specifies a text string to restrict GPOs retrieve.

If GPOFilter is XenApp, then every GPO in the ADDomain or OrganizationalUnit

That has "XenApp" anywhere in the name is backed up and has reports

generated.

Default is all GPOs.

.PARAMETER MaxZipSize

Specifies the maximum size, in MB, of the two Zip files created.

Default is 150MB which is the limit of Outlook 365 email attachments.

https://technet.microsoft.com/en-us/library/exchange-online-limits.aspx#MessageLimits

.PARAMETER OrganizationalUnit

Restricts the retrieval of computer accounts to a specific OU tree.

Must be entered in Distinguished Name format. i.e. OU=XenDesktop,DC=domain,DC=tld.

The script GPOs from the top level OU and all sub-level OUs.

If both ADDomain and OrganizationalUnit are used, the latter takes preference.

Alias OU

.PARAMETER Rename

If a GPO name contains any the following characters, <>:"\/|?*, replace the character

with an _ (Underscore), allowing the creation of a report file in Windows.

The GPO is then renamed in the Group Policy Management Console and Active Directory.


The default is False.

.PARAMETER SmtpServer

Specifies the optional email server to send the output report.

.PARAMETER SmtpPort

Specifies the SMTP port.

The default is 25.

.PARAMETER UseSSL

Specifies whether to use SSL for the SmtpServer.

The default is False.

.PARAMETER From

Specifies the username for the From email address.

If SmtpServer is used, this is a required parameter.

.PARAMETER To

Specifies the username for the To email address.

If SmtpServer is used, this is a required parameter.

.PARAMETER Dev

Clears errors at the beginning of the script.

Outputs all errors to a text file at the end of the script.

This is used when the script developer requests more troubleshooting data.

The text file is placed in the same folder from where the script is run.

This parameter is disabled by default.

.PARAMETER ScriptInfo

Outputs information about the script to a text file.

The text file is placed in the same folder from where the script is run.

This parameter is disabled by default.

This parameter has an alias of SI.

.PARAMETER Log

Generates a log file for troubleshooting.

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1

ComputerName = $Env:USERDNSDOMAIN

ADDomain = $Env:USERDNSDOMAIN

Folder = $pwd

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -ComputerName PDCeDC

ComputerName = PDCeDC

ADDomain = $Env:USERDNSDOMAIN

Folder = $pwd

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -ComputerName ChildPDCeDC

-ADDomain ChildDomain.com

Assuming the script is run from the parent domain.

ComputerName = ChildPDCeDC

ADDomain = ChildDomain.com

Folder = $pwd

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -ComputerName ChildPDCeDC

-ADDomain ChildDomain.com -Folder c:\GPOReports

Assuming the script is run from the parent domain.

ComputerName = ChildPDCeDC

ADDomain = ChildDomain.com

Folder = C:\GPOReports (C:\GPOReports must already exist)

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -SmtpServer mail.domain.tld

-From XDAdmin@domain.tld -To ITGroup@domain.tld

The script will use the email server mail.domain.tld, sending from

XDAdmin@domain.tld, sending to ITGroup@domain.tld.

The script will use the default SMTP port 25 and will not use SSL.

If the current user's credentials are not valid to send email,

the user will be prompted to enter valid credentials.

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -SmtpServer smtp.office365.com

-SmtpPort 587 -UseSSL -From Webster@CarlWebster.com -To ITGroup@CarlWebster.com

The script will use the email server smtp.office365.com on port 587 using SSL,

sending from webster@carlwebster.com, sending to ITGroup@carlwebster.com.

If the current user's credentials are not valid to send email,

the user will be prompted to enter valid credentials.

.EXAMPLE

PS c:\PSScript > .\Get-GPOBackupAndReports.ps1 -folder c:\gpobackups

-ou "ou=lab,dc=labaddomain,dc=com" -gpofilter "xenapp"

Processes all GPOs in the OU "lab" in the domain LabAdomain.com

that contain "xenapp" anywhere in the GPO name.

.EXAMPLE

PS c:\PSScript > .\Get-GPOBackupAndReports.ps1 -folder c:\gpobackups

-ou "ou=lab,dc=labaddomain,dc=com"

Processes all GPOs in the OU "lab" in the domain LabAdomain.com.

.EXAMPLE

PS c:\PSScript > .\Get-GPOBackupAndReports.ps1 -folder c:\gpobackups

-ou "ou=lab,dc=labaddomain,dc=com" -GPOFilter ""

Processes all GPOs in the OU "lab" in the domain LabADDomain.com.

.EXAMPLE

PS c:\PSScript > .\Get-GPOBackupAndReports.ps1 -folder c:\gpobackups

-ou ""

Processes all GPOs in all OUs in the default domain of $Env:USERDNSDOMAIN.

.EXAMPLE

PS C:\PSScript > .\Get-GPOBackupAndReports.ps1 -Rename

ComputerName = $Env:USERDNSDOMAIN

ADDomain = $Env:USERDNSDOMAIN

Folder = $pwd

Replace Windows invalid filename charcters in the GPO name with an _ (Underscore).

The GPO is then renamed in the Group Policy Management Console and Active Directory.

.INPUTS

None. You cannot pipe objects to this script.

.OUTPUTS

No objects are output from this script.

.NOTES

NAME: Get-GPOBackupAndReports.ps1

VERSION: 1.23

AUTHOR: Carl Webster

LASTEDIT: September 12, 2019

#>



[CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = "None", DefaultParameterSetName = "Default") ]


Param(

[parameter(Mandatory=$False)]

[string]$ADDomain=$Env:USERDNSDOMAIN,


[parameter(Mandatory=$False)]

[Alias("ServerName")]

[string]$ComputerName=$Env:USERDNSDOMAIN,

[parameter(Mandatory=$False)]

[string]$Folder="",

[parameter(Mandatory=$False)]

[string]$GPOFilter="",

[parameter(Mandatory=$False)]

[int]$MaxZipSize=150,

[parameter(ParameterSetName="Default",Mandatory=$False)]

[Alias("OU")]

[string]$OrganizationalUnit="",

[parameter(ParameterSetName="Default",Mandatory=$False)]

[switch]$Rename=$False,


[parameter(ParameterSetName="SMTP",Mandatory=$True)]

[string]$SmtpServer="",


[parameter(ParameterSetName="SMTP",Mandatory=$False)]

[int]$SmtpPort=25,


[parameter(ParameterSetName="SMTP",Mandatory=$False)]

[switch]$UseSSL=$False,


[parameter(ParameterSetName="SMTP",Mandatory=$True)]

[string]$From="",


[parameter(ParameterSetName="SMTP",Mandatory=$True)]

[string]$To="",


[parameter(Mandatory=$False)]

[Switch]$Dev=$False,

[parameter(Mandatory=$False)]

[Alias("SI")]

[Switch]$ScriptInfo=$False,

[parameter(Mandatory=$False)]

[Switch]$Log=$False

)


#webster@carlwebster.com

#@carlwebster on Twitter

#http://www.CarlWebster.com

#Created on April 25, 2018


#Version 1.23 12-Sep-2019

# Add a Rename parameter switch. (Thanks to Jani Kohonen for this suggestion)

# If a GPO name contains any the following characters, <>:"\/|?*, replace the character

# with an _ (Underscore), allowing the creation of a report file in Windows.

# The GPO is then renamed in the Group Policy Management Console and Active Directory.

# The default is False.


#Version 1.22 24-Apr-2019

# Fixed bug when a GPO had been linked multiple times. The script counted all the GPOs,

# even the duplicates. Added -Unique to the Sort.

# If a GPO was linked ten times, the script backed up the GPO ten times and created

# the reports ten times.

#

#Version 1.21 11-Apr-2019

# If -OrganizationUnit parameter was used, the $ADDomain paramater was blanked out and

# not available for the Zip file creation. IF $ADDomain is blank, set $ADDomain to

# $Env:USERDNSDOMAIN

#

#Version 1.20 29-Mar-2019

# Received performance tuning help from Michael B. Smith for this update

# Add requirement for the ActiveDirectory module

# Add simple error checking to the backup and report creation

# Add two parameters: GPOFilter and OrganizationalUnit

# Adding testing for illegal filename characters '<>:"\/|?*' in the GPO name before

# creating the HTML and XML report files.

# If both ADDomain and OrganizationalUnit are specified, set ADDomain to empty string

# Updated Function ProcessScriptEnd with new parameters

# Updated help text

#

#Version 1.10 1-Jun-2018

#

# Add Parameter MaxZipSize (in MBs)

# Test combined size of the two Zip files created to see if <= to MaxZipSize

# If > MaxZipSize, do not attempt the email

# Backup GPOs one at a time

# Show the name of each GPO being backed up to show the GPO causing a backup error

#

#Version 1.0 released to the community on 1-May-2018

#



Set-StrictMode -Version 2


$PSDefaultParameterValues = @{"*:Verbose"=$True}

$SaveEAPreference = $ErrorActionPreference

$ErrorActionPreference = 'SilentlyContinue'


#If $OrganizationalUnit is used, blank out $ADDomain

If(![String]::IsNullOrEmpty($ADDomain) -and ![String]::IsNullOrEmpty($OrganizationalUnit)) #V1.20

{

#OrganizationalUnit was specified, blank out ADDomain

$ADDomain = ""

}


#region check for DA and elevatation

Function UserIsaDomainAdmin

{

#function adapted from sample code provided by Thomas Vuylsteke

$IsDA = $False

$name = $env:username

If([String]::IsNullOrEmpty($ADDomain))

{

$ADDomain = $Env:USERDNSDOMAIN

}

Write-Verbose "$(Get-Date): TokenGroups - Checking groups for $name"


$root = [ADSI]""

$filter = "(sAMAccountName=$name)"

$props = @("distinguishedName")

$Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)

$account = $Searcher.FindOne().properties.distinguishedname


$user = [ADSI]"LDAP://$Account"

$user.GetInfoEx(@("tokengroups"),0)

$groups = $user.Get("tokengroups")


$domainAdminsSID = New-Object System.Security.Principal.SecurityIdentifier (((Get-ADDomain -Server $ADDomain -EA 0).DomainSid).Value+"-512")


ForEach($group in $groups)

{

$ID = New-Object System.Security.Principal.SecurityIdentifier($group,0)

If($ID.CompareTo($domainAdminsSID) -eq 0)

{

$IsDA = $True

Break

}

}


Return $IsDA

}


Function ElevatedSession

{

$currentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent() )


If($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ))

{

Write-Verbose "$(Get-Date): This is an elevated PowerShell session"

Return $True

}

Else

{

Write-Host "" -Foreground White

Write-Host "$(Get-Date): This is NOT an elevated PowerShell session" -Foreground White

Write-Host "" -Foreground White

Return $False

}

}


#region script setup function

Function ProcessScriptSetup

{

$Script:GPOStartTime = Get-Date


#make sure user is running the script with Domain Admin rights

Write-Verbose "$(Get-Date): Testing to see if $env:username has Domain Admin rights"

$AmIReallyDA = UserIsaDomainAdmin

If($AmIReallyDA -eq $True)

{

#user has Domain Admin rights

Write-Verbose "$(Get-Date): $env:username has Domain Admin rights in the $ADDomain Domain"

}

Else

{

#user does not have Domain Admin rights

Write-Error "$(Get-Date): $env:username does not have Domain Admin rights in the $ADDomain Domain. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

$Elevated = ElevatedSession

If( -not $Elevated )

{

Write-Error "This is not an elevated PowerShell session. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}


#if computer name is localhost, get actual server name

If($ComputerName -eq "localhost")

{

$ComputerName = $env:ComputerName

Write-Verbose "$(Get-Date): Server name has been changed from localhost to $($ComputerName)"

}

#see if default value of $Env:USERDNSDOMAIN was used

If($ComputerName -eq $Env:USERDNSDOMAIN)

{

#change $ComputerName to a found global catalog server

$Results = (Get-ADDomainController -DomainName $ADDomain -Discover -Service GlobalCatalog -EA 0).Name

If($? -and $Null -ne $Results)

{

$ComputerName = $Results

Write-Verbose "$(Get-Date): Server name has been changed from $Env:USERDNSDOMAIN to $ComputerName"

}

ElseIf(!$?) #changed for 2.16

{

#may be in a child domain where -Service GlobalCatalog doesn't work. Try PrimaryDC

$Results = (Get-ADDomainController -DomainName $ADDomain -Discover -Service PrimaryDC -EA 0).Name


If($? -and $Null -ne $Results)

{

$ComputerName = $Results

Write-Verbose "$(Get-Date): Server name has been changed from $Env:USERDNSDOMAIN to $ComputerName"

}

}

}


#if computer name is an IP address, get host name from DNS

#http://blogs.technet.com/b/gary/archive/2009/08/29/resolve-ip-addresses-to-hostname-using-powershell.aspx

#help from Michael B. Smith

$ip = $ComputerName -as [System.Net.IpAddress]

If($ip)

{

$Result = [System.Net.Dns]::gethostentry($ip)

If($? -and $Null -ne $Result)

{

$ComputerName = $Result.HostName

Write-Verbose "$(Get-Date): Server name has been updated from $ip to $ComputerName"

}

Else

{

Write-Warning "Unable to resolve $ComputerName to a hostname"

}

}

Else

{

#server is online but for some reason $ComputerName cannot be converted to a System.Net.IpAddress

}


If(![String]::IsNullOrEmpty($ComputerName))

{

#get server name

#first test to make sure the server is reachable

Write-Verbose "$(Get-Date): Testing to see if $ComputerName is online and reachable"

If(Test-Connection -ComputerName $ComputerName -quiet -EA 0)

{

Write-Verbose "$(Get-Date): Server $ComputerName is online."

Write-Verbose "$(Get-Date): `tTest #1 to see if $ComputerName is a Domain Controller."

#the server may be online but is it really a domain controller?


#is the ComputerName in the current domain

$Results = Get-ADDomainController $ComputerName -EA 0

If(!$? -or $Null -eq $Results)

{

#try using the Forest name

Write-Verbose "$(Get-Date): `tTest #2 to see if $ComputerName is a Domain Controller."

$Results = Get-ADDomainController $ComputerName -Server $ADDomain -EA 0

If(!$?)

{

$ErrorActionPreference = $SaveEAPreference

Write-Error "`n`n`t`t$ComputerName is not a domain controller for $ADDomain.`n`t`tScript cannot continue.`n`n"

$ErrorActionPreference = $SaveEAPreference

Exit

}

Else

{

Write-Verbose "$(Get-Date): `tTest #2 succeeded. $ComputerName is a Domain Controller."

}

}

Else

{

Write-Verbose "$(Get-Date): `tTest #1 succeeded. $ComputerName is a Domain Controller."

}

$Results = $Null

}

Else

{

Write-Error "Computer $ComputerName is offline.`nScript cannot continue.`n`n"

$ErrorActionPreference = $SaveEAPreference

Exit

}

}


If(![String]::IsNullOrEmpty($ADDomain))

{

If([String]::IsNullOrEmpty($ComputerName))

{

$results = Get-ADDomain -Identity $ADDomain -EA 0

If(!$?)

{

Write-Error "Could not find a domain identified by: $ADDomain.`nScript cannot continue.`n`n"

$ErrorActionPreference = $SaveEAPreference

Exit

}

}

Else

{

$results = Get-ADDomain -Identity $ADDomain -Server $ComputerName -EA 0


If(!$?)

{

Write-Error "Could not find a domain with the name of $ADDomain.`n`n`t`tScript cannot continue.`n`n`t`tIs $ComputerName running Active Directory Web Services?"

$ErrorActionPreference = $SaveEAPreference

Exit

}

}

Write-Verbose "$(Get-Date): $ADDomain is a valid domain name"

}

ElseIf(![String]::IsNullOrEmpty($OrganizationalUnit))

{

Write-Verbose "$(Get-Date): Validating Organnization Unit"

try

{

$results = Get-ADOrganizationalUnit -Identity $OrganizationalUnit

}

catch

{

#does not exist

Write-Error "Organization Unit $OrganizationalUnit does not exist.`n`nScript cannot continue`n`n"

Exit

}

Write-Verbose "$(Get-Date): $OrganizationalUnit is a valid OU"

}


If($Folder -ne "")

{

Write-Verbose "$(Get-Date): Testing folder path"

#does it exist

If(Test-Path $Folder -EA 0)

{

#it exists, now check to see if it is a folder and not a file

If(Test-Path $Folder -pathType Container -EA 0)

{

#it exists and it is a folder

Write-Verbose "$(Get-Date): Folder path $Folder exists and is a folder"

}

Else

{

#it exists but it is a file not a folder

Write-Error "Folder $Folder is a file, not a folder. Script cannot continue"

$ErrorActionPreference = $SaveEAPreference

Exit

}

}

Else

{

#does not exist

Write-Error "Folder $Folder does not exist. Script cannot continue"

$ErrorActionPreference = $SaveEAPreference

Exit

}

}


If($Folder -eq "")

{

$Script:pwdPath = $pwd.Path

}

Else

{

$Script:pwdPath = $Folder

}


If($Script:pwdPath.EndsWith("\"))

{

#remove the trailing \

$Script:pwdPath = $Script:pwdPath.SubString(0, ($Script:pwdPath.Length - 1))

}


If($Dev)

{

$Error.Clear()

$Script:DevErrorFile = "$($Script:pwdPath)\Get-GPOBackupAndReportsScriptErrors_$(Get-Date -f yyyy-MM-dd_HHmm).txt"

}


If($Log)

{

#start transcript logging

$Script:LogPath = "$($Script:pwdPath)\Get-GPOBackupAndReportsScriptTranscript_$(Get-Date -f yyyy-MM-dd_HHmm).txt"

try

{

Start-Transcript -Path $Script:LogPath -Force -Verbose:$false | Out-Null

Write-Verbose "$(Get-Date): Transcript/log started at $Script:LogPath"

$Script:StartLog = $true

}

catch

{

Write-Verbose "$(Get-Date): Transcript/log failed at $Script:LogPath"

$Script:StartLog = $false

}

}


[string]$Script:RunningOS = (Get-WmiObject -class Win32_OperatingSystem -EA 0).Caption


#enter the email creds

If(![System.String]::IsNullOrEmpty( $SmtpServer ))

{

$Script:emailCredentials = Get-Credential -Message "Enter the email account and password to send email"

}

}


#region email function

Function SendEmail

{

Param([array]$Attachments)

Write-Verbose "$(Get-Date): Prepare to email"

$Success = $True

$emailAttachment = $Attachments

$emailSubject = "GPO Backup and Reports for $ADDomain"

$emailBody = @"

Hello, <br />

<br />

$emailsubject is attached.

"@


If($Dev)

{

Out-File -FilePath $Script:DevErrorFile -InputObject $error 4>$Null

}


$error.Clear()


If($UseSSL)

{

Write-Verbose "$(Get-Date): Trying to send email using current user's credentials with SSL"

Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From `

-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To `

-UseSSL -credential $emailCredentials *>$Null

}

Else

{

Write-Verbose "$(Get-Date): Trying to send email using current user's credentials without SSL"

Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From `

-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To -credential $emailCredentials *>$Null

}


$e = $error[0]


If($e.Exception.ToString().Contains("5.7.57"))

{

#The server response was: 5.7.57 SMTP; Client was not authenticated to send anonymous mail during MAIL FROM

Write-Verbose "$(Get-Date): Current user's credentials failed. Ask for usable credentials."


If($Dev)

{

Out-File -FilePath $Script:DevErrorFile -InputObject $error -Append 4>$Null

}


$error.Clear()


$emailCredentials = Get-Credential -Message "Enter the email account and password to send email"


If($UseSSL)

{

Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From `

-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To `

-UseSSL -credential $emailCredentials *>$Null

}

Else

{

Send-MailMessage -Attachments $emailAttachment -Body $emailBody -BodyAsHtml -From $From `

-Port $SmtpPort -SmtpServer $SmtpServer -Subject $emailSubject -To $To `

-credential $emailCredentials *>$Null

}


$e = $error[0]


If($? -and $Null -eq $e)

{

Write-Verbose "$(Get-Date): Email successfully sent using new credentials"

}

Else

{

Write-Verbose "$(Get-Date): Email was not sent:"

Write-Warning "$(Get-Date): Exception: $e.Exception"

$Success = $False

}

}

Else

{

Write-Verbose "$(Get-Date): Email was not sent:"

Write-Warning "$(Get-Date): Exception: $e.Exception"

$Success = $False

}


If($Success)

{

Return $True

}

Else

{

Return $False

}

}


#region main function

Function GetGpoBackupAndReports

{

If(![String]::IsNullOrEmpty($OrganizationalUnit))

{

Write-Verbose "$(Get-Date): Retrieving all GPOs in OU $OrganizationalUnit"

$GPOs = New-Object System.Collections.ArrayList

$LinkedGPOs = Get-ADOrganizationalUnit -Filter * -SearchBase $OrganizationalUnit -SearchScope Subtree | Select-Object -ExpandProperty LinkedGroupPolicyObjects

If($? -and $Null -ne $LinkedGPOs)

{

ForEach($LinkedGPO in $LinkedGPOs)

{

$GpoName = ([adsi]"LDAP://$LinkedGPO").DisplayName.ToString()

If($GpoName -like "*$GpoFilter*")

{

$GPOs.Add($GPOName) > $Null

}

}

}

ElseIf($? -and $Null -eq $LinkedGPOs)

{

Write-Warning "No GPOs were found. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

Else

{

Write-Error "Unable to retrieve GPOs. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

}

ElseIf(![String]::IsNullOrEmpty($ADDomain))

{

Write-Verbose "$(Get-Date): Retrieving all GPOs in $ADDomain"

$GPOs = New-Object System.Collections.ArrayList

$results = @(Get-GPO -All -EA 0)

If($? -and $Null -ne $results)

{

$results | Where-Object { $_.DisplayName -like "*$GpoFilter*" } | ForEach-Object { $null = $GPOs.Add( $_.DisplayName ) }

}

ElseIf($? -and $Null -eq $results)

{

Write-Warning "No GPOs were found. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

Else

{

Write-Error "Unable to retrieve GPOs. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

}


If($Null -ne $GPOs)

{

$GPOs = $GPOs | Sort-Object -Unique

[int]$GPOCnt = $GPOs.Count


Write-Verbose "$(Get-Date): Creating Backup and Reports folders"

$ScriptDir = $Script:pwdPath

$BackupDir = "$($ScriptDir)\GPOBackups"

$ReportsDir = "$($ScriptDir)\GPOReports"

New-Item -Path $BackupDir -Force -ItemType "directory" *> $Null

New-Item -Path $ReportsDir -Force -ItemType "directory" *> $Null


Write-Verbose "$(Get-Date): Backing up $GPOCnt GPOs to $BackupDir"

#V1.10, backup individual GPO to show which GPO causes the backup to fail

ForEach($GPO in $GPOs)

{

Write-Verbose "$(Get-Date): Backing up GPO $GPO"


$Null = Backup-GPO -Name $GPO -Path $BackupDir -EA 0 *> $Null

If(!($?))

{

Write-Warning "`t`t`tUnable to backup GPO $($GPO): $error[0].exception.ToString()"

}

}


ForEach($GPO in $GPOs)

{

Write-Verbose "$(Get-Date): Creating reports for GPO $GPO"

#Version 1.20, add checking the GPO name for illegal filename characters.

#for GPO backups, the GUID is used, for GPO reports, the GPO name is used.

#you can use characters in a GPO name that are illegal for Windows filenames.

#Using any the following characters '<>:"\/|?*' in a GPO name means Windows

#can't create an HTML or XML file for the GPO report.

$Skip = $False #V1.23

If($GPO.IndexOfAny( '<>:"\/|?*' ) -ge 0)

{

If($Rename -eq $False) #default behaviour

{

$Skip = $True #V1.23

Write-Host "WARNING: GPO `"$GPO`" contains illegal filename characters. No report created." -Foreground White

}

ElseIf($Rename -eq $True) #V1.23 new option

{

Write-Host "GPO `"$GPO`" contains illegal filename characters. Replacing with _" -Foreground White

#replace each of the invalid characters with an _ (underscore)

$NewGPOName = $GPO

$NewGPOName = $NewGPOName.Replace('<','_')

$NewGPOName = $NewGPOName.Replace('>','_')

$NewGPOName = $NewGPOName.Replace(':','_')

$NewGPOName = $NewGPOName.Replace('"','_')

$NewGPOName = $NewGPOName.Replace('\','_')

$NewGPOName = $NewGPOName.Replace('/','_')

$NewGPOName = $NewGPOName.Replace('|','_')

$NewGPOName = $NewGPOName.Replace('?','_')

$NewGPOName = $NewGPOName.Replace('*','_')

Write-Host "New GPO name is `"$NewGPOName`"" -Foreground White

$results = Rename-GPO -Name $GPO -TargetName $NewGPOName

If($? -and $Null -ne $results)

{

Write-Host ""

Write-Host "GPO successfully renamed" -Foreground White

Write-Host ""

$GPO = $NewGPOName

}

}

}

If($Skip -eq $False) #V1.23

{

try

{

$Results = Get-GPOReport -Name $GPO -ReportType HTML -Path "$($ReportsDir)\$($GPO).html"

}

catch

{

Write-Warning "Unable to create an HTML report for GPO $GPO : $error[0].Exception.ToString()"

}


try

{

$Results = Get-GPOReport -Name $GPO -ReportType XML -Path "$($ReportsDir)\$($GPO).xml"

}

catch

{

Write-Warning "Unable to create an XML report for GPO $GPO : $error[0].exception.ToString()"

}

}

}

}

ElseIf($Null -eq $GPOs)

{

Write-Warning "No GPOs were found. Script cannot continue."

$ErrorActionPreference = $SaveEAPreference

Exit

}

}


#region script end

Function ProcessScriptEnd

{

Write-Verbose "$(Get-Date): Script has completed"

Write-Verbose "$(Get-Date): "


Write-Verbose "$(Get-Date): Creating First Zip File"

$ScriptDir = $Script:pwdPath

$BackupDir = "$($ScriptDir)\GPOBackups"

$ReportsDir = "$($ScriptDir)\GPOReports"

If([String]::IsNullOrEmpty($ADDomain)) #added V1.21

{

$ADDomain = $Env:USERDNSDOMAIN

}


[Reflection.Assembly]::LoadWithPartialName( "System.IO.Compression.FileSystem" ) > $Null

[System.AppDomain]::CurrentDomain.GetAssemblies() > $Null

$src_folder = $BackupDir

$destfile1 = "$ScriptDir\_GetGPOBackup_$($ADDomain).zip"

$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

$includebasedir = $true

[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder, $destfile1, $compressionLevel, $includebasedir) > $Null


Write-Verbose "$(Get-Date): Creating second Zip File"

$src_folder = $ReportsDir

$destfile2 = "$ScriptDir\_GetGPOReports_$($ADDomain).zip"

$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal

$includebasedir = $true

[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder, $destfile2, $compressionLevel, $includebasedir) > $Null


#email zip files if requested

If(![System.String]::IsNullOrEmpty( $SmtpServer ))

{

#V1.10, check the size of the combined zip files to see if the size is smaller than MaxZipSize (150MB by default)

$Zip1Size = ([System.IO.FileInfo]$destfile1).Length

$Zip1Size = ([math]::round(($Zip1Size/1MB),3))


$Zip2Size = ([System.IO.FileInfo]$destfile2).Length

$Zip2Size = ([math]::round(($Zip2Size/1MB),3))

$TotalZipSize = $Zip1Size + $Zip2Size


If($TotalZipSize -le $MaxZipSize)

{

Write-Verbose "$(Get-Date): Combined size of the two zip files is $TotalZipSize MB. Proceeding with email attempt."

Write-Verbose "$(Get-Date): Sending email"

$emailattachment = @()

$emailAttachment += $destfile1

$emailAttachment += $destfile2

$MailSuccess = SendEmail $emailAttachment

If($MailSuccess)

{

Write-Verbose "$(Get-Date): Email sent"

}

Else

{

Write-Host "$(Get-Date): Unable to send $emailAttachment via email" -ForegroundColor Red

Write-Host "$(Get-Date): Please send $emailAttachment to $To" -ForegroundColor Red

}

}

Else

{

Write-Verbose "$(Get-Date): Combined size of the two zip files is $TotalZipSize. Cancel email attempt."

Write-Host "$(Get-Date): Unable to send $emailAttachment via email because of MaxZipSize limit" -ForegroundColor Red

Write-Host "$(Get-Date): Please send $emailAttachment to $To" -ForegroundColor Red

}

}


Write-Verbose "$(Get-Date): Script started: $($Script:GPOStartTime)"

Write-Verbose "$(Get-Date): Script ended: $(Get-Date)"

$runtime = $(Get-Date) - $Script:GPOStartTime

$Str = [string]::format("{0} days, {1} hours, {2} minutes, {3}.{4} seconds",

$runtime.Days,

$runtime.Hours,

$runtime.Minutes,

$runtime.Seconds,

$runtime.Milliseconds)

Write-Verbose "$(Get-Date): Elapsed time: $($Str)"


If($Dev)

{

Out-File -FilePath $Script:DevErrorFile -InputObject $error -Append 4>$Null

}


If($ScriptInfo)

{

$SIFile = "$($Script:pwdPath)\GetGpoBackupAndReportsScriptInfo_$(Get-Date -f yyyy-MM-dd_HHmm).txt"

Out-File -FilePath $SIFile -InputObject "" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "ComputerName : $ComputerName" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "Dev : $Dev" 4>$Null

If($Dev)

{

Out-File -FilePath $SIFile -Append -InputObject "DevErrorFile : $Script:DevErrorFile" 4>$Null

}

Out-File -FilePath $SIFile -Append -InputObject "Domain name : $ADDomain" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "Folder : $Folder" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "GPO Filter : $GPOFilter" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "Log : $Log" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "Organizational unit: $OrganizationalUnit" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "Script Info : $ScriptInfo" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "OS Detected : $Script:RunningOS" 4>$Null

Out-File -FilePath $SIFile -Append -InputObject "PoSH version : $($Host.Version)" 4>$Null

}


#stop transcript logging

If($Log -eq $True)

{

If($Script:StartLog -eq $true)

{

try

{

Stop-Transcript | Out-Null

Write-Verbose "$(Get-Date): $Script:LogPath is ready for use"

}

catch

{

Write-Verbose "$(Get-Date): Transcript/log stop failed"

}

}

}

$ErrorActionPreference = $SaveEAPreference

}


#region script core

#Script begins


ProcessScriptSetup


GetGpoBackupAndReports


#region finish script

Write-Verbose "$(Get-Date): Finishing up script"

#end of document processing


ProcessScriptEnd

[gc]::collect()


 
 
 

Recent Posts

See All
PowerShell - List All Domain SPNs

Save as same List_ALL_SPNs.ps1 or similar, this LDap calls the Domain for all Service Principal names and accounts related #Build LDAP...

 
 
 
PowerShell - Start-Monitoring

This is a great script used to Monitor and Email if a server is up or down, once ran, and smtp and from address is set, then run:...

 
 
 

Comments


Post: Blog2 Post
  • Facebook
  • Twitter
  • LinkedIn

©2022 by Boyette Technical Services. Proudly created with Wix.com

bottom of page