PowerShell: Windows Systems State Backup Daily

# Windows-Systems-State-Backup-V0.1.ps1
#
# What this script does:
# 1. Create a Windows Scheduled task on an Active Directory controller to perform daily backups at a predetermined time
# 2. Output a log to include backup duration and result of last backup
# 3. Remove backup files that are outside of the retention period
#
# Requirements:
# Powershell 3.0 is necessary to avoid this error, "the term 'Register-ScheduledJob' is not recognized as the name of a cmdlet"

# Set variables:
$taskName="System State Backup";
$storageUncPath="\\Backups\ActiveDirectory\";
$frequency="Daily";
$executionTime="2:00AM";
$retentionDays=7;
$pdcServer=(Get-ADForest |Select-Object -ExpandProperty RootDomain |Get-ADDomain |Select-Object -Property PDCEmulator).PDCEmulator;

# Optional: obtain a domain admin service account credentials
$prompt = "Username and Password of Domain Admin"
$cred = $Host.UI.PromptForCredential("Credential",$prompt,"$env:userdomain\$env:username",$env:userdomain)

# Ensure that uncPath has a trailing backslash
if ($storageUncPath[$storageUncPath.length-1] -ne "\"){$storageUncPath+="\";}

Function createBackupScheduledTask{
param(
$taskName="System State Backup",
$storageUncPath,
$frequency="Daily",
$time="2:00AM",
$retentionDays=7,
$cred=$credential
)

Function removeBackupScheduledTask{
param(
$taskName="System State Backup"
)
Unregister-ScheduledTask $taskname -confirm:$false -ErrorAction SilentlyContinue
Write-Host "Taskname $taskName is deleted."
$checkJob=Get-ScheduledJob $taskname -ErrorAction SilentlyContinue #This command updates the metadata related to deleted task to fully void its remnants
}

$backupScheduledTask=Get-ScheduledTask $taskName -ErrorAction SilentlyContinue
if ($backupScheduledTask){write-host "$taskName already exists. Program will now remove the previous task instance.";removeBackupScheduledTask;}

Do {
Try{
$taskCreated=Register-ScheduledJob -ErrorAction Stop -Name $taskName -ScriptBlock{
param($storageUncPath,$retentionDays)

# Start a timer
$timer=[System.Diagnostics.Stopwatch]::StartNew();

# Setup backup storage location
$backupDirectory=$storageUncPath;
$dateStamp=Get-Date -Format yyyy-MM-dd;
$logFile=$backupDirectory+"backup-log.txt";
$dateStampedFolder=$backupDirectory+$dateStamp;
New-Item -ItemType Directory $dateStampedFolder -Force;
$backupStorageLocation = New-WBBackupTarget -Network $dateStampedFolder;

# Instantiate a new policy
$thisPolicy = New-WBPolicy;

#Add System State to the policy
Add-WBSystemState -Policy $thisPolicy;

#Add backup location to policy
Add-WBBackupTarget -Policy $thisPolicy -Target $backupStorageLocation;

#Start backup using policy
Start-WBBackup -Policy $thisPolicy;

# Cleanup folders that are outside of retention period
$thisComputer=$env:computername;
if($backupDirectory -ne $null){
$expiredFolders=(get-childitem $backupDirectory|?{ $_.PSIsContainer }|?{$_.LastWriteTime -lt (get-date).AddDays(-$retentionDays)}|Select FullName).FullName;
}else{
$expiredFolders=$null;
}
if($expiredFolders){
$removedBackupSets = $expiredFolders|%{Remove-WBBackupSet -NetworkPath $_ -ComputerName $thisComputer -Force}
if($removedBackupSets){$expiredFolders|%{for ($i=0;$i -lt 2;$i++){try{remove-item -LiteralPath $_ -Force -Recurse}catch{}}};} #First pass, delete children; second pass, delete parent
}

#Append result into a log
#$lastBackupTime=(Get-WinEvent -LogName Microsoft-Windows-Backup -FilterXPath "*[System[EventID=4]]"|select -First 1).TimeCreated;
$lastBackupTime=(Get-WBBackupSet|select -last 1).BackupTime;
$hours=[math]::Round(($timer.Elapsed.TotalSeconds)/3600,2);
Add-Content $logFile "`r`n$lastBackupTime` --- $thisComputer backup completed in $hours hours.`r`n$removedBackupSets";
} -ScheduledJobOption $(New-ScheduledJobOption -RunElevated) -Trigger @{Frequency = $frequency; At=$time} -ArgumentList $storageUncPath,$retentionDays -Credential $cred;
if ($taskCreated){write-host "$taskName to run $frequency at $time has been successfully created.";};
}
catch{
$taskCreated=$false;
write-host "Waiting 10 seconds for next retry...";
$checkJob=Get-ScheduledJob $taskname -ErrorAction SilentlyContinue;
sleep 10;
continue;
}
}while($taskCreated -eq $false)
}

#createBackupScheduledTask -taskName $taskName -storageUncPath $storageUncPath -frequency $frequency -time $executionTime -retention $retentionDays

if($pdcServer){
invoke-command -Credential $cred -computername $pdcServer -scriptBlock{
param($importedFunc,$variable1,$variable2,$variable3,$variable4,$variable5)
$credential=$using:cred
[ScriptBlock]::Create($importedFunc).invoke($variable1,$variable2,$variable3,$variable4,$variable5,$credential);
} -Args ${function:createBackupScheduledTask},$taskName,$storageUncPath,$frequency,$executionTime,$retentionDays
}else{write-host "set pdcServer variable before proceeding"}

 

Sample execution result:

System State Backup already exists. Program will now remove the previous task instance.
Taskname System State Backup is deleted.
System State Backup to run Daily at 2:00AM has been successfully created.

Leave a Reply

Your email address will not be published. Required fields are marked *