Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This script checks recent Azure Automation jobs for your Global Secure Access configuration backup runbook. It returns a compliance summary and can send an alert email when the backup job fails or no job ran during the expected schedule.
The script dot-sources _GsaOpsHelpers.ps1, so place the helper file in the same folder before you run this sample.
Prerequisites
- PowerShell 7.0 or later.
- Install the modules listed in the script's
.NOTESblock. - Use only permissions and roles that your organization has approved for the task.
Script
<#
.SYNOPSIS
Monitors GSA configuration backup job results and alerts on failures.
.DESCRIPTION
Queries Azure Automation for the status of GSA backup runbook jobs over a
configurable lookback window. If any jobs failed or no jobs ran during the
expected schedule, sends an alert email via Microsoft Graph.
Run this script daily as a secondary watchdog runbook in the same (or a
different) Automation Account.
.PARAMETER AutomationAccountName
Name of the Azure Automation Account running the backup runbooks.
.PARAMETER ResourceGroupName
Resource group containing the Automation Account.
.PARAMETER BackupRunbookName
Name of the runbook that performs GSA configuration backups.
.PARAMETER LookbackHours
Number of hours to look back for completed jobs. Default: 26 (covers a
daily schedule with 2-hour buffer).
.PARAMETER AlertRecipient
Email address to receive failure alerts.
.PARAMETER SenderId
UserId or UPN of the mailbox used to send alert emails.
.EXAMPLE
.\Test-GsaBackupCompliance.ps1 -AutomationAccountName "gsa-automation" -ResourceGroupName "gsa-ops-rg" -BackupRunbookName "Export-GsaConfiguration" -AlertRecipient "gsa-ops@contoso.com" -SenderId "gsa-automation@contoso.com"
.NOTES
Required permissions: Azure — Automation Job Operator on the Automation Account; Graph — Mail.Send
Minimum module versions: Az.Accounts 2.x, Az.Automation 1.x, Microsoft.Graph.Authentication 2.x, Microsoft.Graph.Users.Actions 2.x
Dot-sources scripts/automation/_GsaOpsHelpers.ps1 for shared auth and email helpers.
Author: GSA Operations
#>
[CmdletBinding()]
[OutputType([pscustomobject])]
param(
[Parameter(Mandatory)]
[string]$AutomationAccountName,
[Parameter(Mandatory)]
[string]$ResourceGroupName,
[Parameter(Mandatory)]
[string]$BackupRunbookName,
[int]$LookbackHours = 26,
[string]$AlertRecipient,
[string]$SenderId,
[string]$TenantId,
[string]$SubscriptionId,
[switch]$SkipEmail
)
$ErrorActionPreference = 'Stop'
# Load shared helpers (Connect-GsaRuntime, Send-GsaAlertEmail)
. "$PSScriptRoot\_GsaOpsHelpers.ps1"
# Ensure Az and Graph contexts are available (throws on failure)
Connect-GsaRuntime -Service Both -TenantId $TenantId -SubscriptionId $SubscriptionId
# Query recent backup jobs
$cutoff = (Get-Date).AddHours(-$LookbackHours)
try {
$jobs = Get-AzAutomationJob `
-AutomationAccountName $AutomationAccountName `
-ResourceGroupName $ResourceGroupName `
-RunbookName $BackupRunbookName `
-ErrorAction Stop |
Where-Object { $_.CreationTime -ge $cutoff }
} catch {
Write-Error "Failed to query Automation jobs. Error: $_"
return
}
# Evaluate results
$failedJobs = $jobs | Where-Object { $_.Status -eq 'Failed' }
$noJobsRan = $jobs.Count -eq 0
if (-not $noJobsRan -and $failedJobs.Count -eq 0) {
Write-Verbose "All $($jobs.Count) backup job(s) in the last $LookbackHours hours completed successfully."
[PSCustomObject]@{
Status = "Compliant"
CheckedAt = (Get-Date)
TotalJobs = $jobs.Count
FailedJobs = 0
}
return
}
# Build alert details
$alertReason = if ($noJobsRan) {
"No backup jobs ran in the last $LookbackHours hours. The scheduled backup may be misconfigured or the Automation Account may be stopped."
} else {
"$($failedJobs.Count) of $($jobs.Count) backup job(s) failed in the last $LookbackHours hours."
}
$jobDetails = ""
if ($failedJobs.Count -gt 0) {
$tableRows = ($failedJobs | ForEach-Object {
"<tr><td>$($_.JobId)</td><td>$($_.Status)</td><td>$($_.CreationTime.ToString('yyyy-MM-dd HH:mm'))</td><td>$($_.Exception)</td></tr>"
}) -join "`n"
$jobDetails = @"
<table border="1" cellpadding="5" cellspacing="0">
<tr><th>Job ID</th><th>Status</th><th>Started</th><th>Error</th></tr>
$tableRows
</table>
"@
}
$emailBody = @"
<h2>GSA Backup Compliance Alert</h2>
<p>$alertReason</p>
$jobDetails
<p><strong>Action required:</strong></p>
<ol>
<li>Check the Automation Account <strong>$AutomationAccountName</strong> in resource group <strong>$ResourceGroupName</strong>.</li>
<li>Review the failed job output for error details.</li>
<li>Run the backup manually: <code>Start-AzAutomationRunbook -Name '$BackupRunbookName' -AutomationAccountName '$AutomationAccountName' -ResourceGroupName '$ResourceGroupName'</code></li>
<li>Verify the backup file was created in your backup storage location.</li>
<li>Fix the root cause (expired credentials, API throttling, storage quota) and confirm the next scheduled run succeeds.</li>
</ol>
"@
if ($SkipEmail) {
Write-Verbose "Skipping alert email (-SkipEmail specified)."
} elseif (-not $SenderId -or -not $AlertRecipient) {
Write-Warning "Skipping alert email — -SenderId and -AlertRecipient are required when -SkipEmail is not set."
} else {
try {
Send-GsaAlertEmail `
-SenderId $SenderId `
-Recipient $AlertRecipient `
-Subject "GSA Backup Alert — $alertReason" `
-HtmlBody $emailBody
} catch {
Write-Warning "Alert email failed (non-fatal): $_"
}
}
# Return summary
[PSCustomObject]@{
Status = "NonCompliant"
CheckedAt = (Get-Date)
TotalJobs = $jobs.Count
FailedJobs = $failedJobs.Count
Reason = $alertReason
}