Back up Azure Conditional Access Policies with PowerShell and Azure DevOps
November 3, 2022
I’ve been playing a lot with Azure Policies lately. One of my lastest requirements was to be able to export my company’s CA policies.
Below you can find my implementation to automatically back up my policies. I’m also leveraging Azure DevOps pipelines as I wanted something that’s free, would run on a schedule and save files to a Blob Container. That’s a very simple implementation and works very well for my needs.
You can find the source code here: https://github.com/danielvca/ca_policy_bkp
1 Initial Setup and Pre-Requisites
- AZDO Project and repo.
- Blob Container
- AZDO Library for secrets.
- AZDO Service Connection.
The first step is to create a library and input the variables:
We will need the following secrets.
- clientid
- ClientSecret
- strkey
- tenantid
export_ca_.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
#Backup and Export Azure Conditional Access Policies.
# https://azureblog.dev
function GetGraphToken {
Param(
[parameter(Mandatory = $true)]
$clientId,
[parameter(Mandatory = $true)]
$tenantId,
[parameter(Mandatory = $true)]
$clientSecret
)
# Construct URI
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
# Construct Body
$body = @{
client_id = $clientId
scope = "https://graph.microsoft.com/.default"
client_secret = $clientSecret
grant_type = "client_credentials"
}
# Get OAuth 2.0 Token
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
# Access Token
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
#Returns token
return $token
}
function RunQueryandEnumerateResults {
Param(
[parameter(Mandatory = $true)]
[String]
$apiUri,
[parameter(Mandatory = $true)]
$token
)
#Run Graph Query
$Results = (Invoke-RestMethod -Headers @{Authorization = "Bearer $($Token)" } -Uri $apiUri -Method Get)
#Output Results for debug checking
#write-host $results
#Begin populating results
$ResultsValue = $Results.value
#If there is a next page, query the next page until there are no more pages and append results to existing set
if ($results."@odata.nextLink" -ne $null) {
write-host enumerating pages -ForegroundColor yellow
$NextPageUri = $results."@odata.nextLink"
##While there is a next page, query it and loop, append results
While ($NextPageUri -ne $null) {
$NextPageRequest = (Invoke-RestMethod -Headers @{Authorization = "Bearer $($Token)" } -Uri $NextPageURI -Method Get)
$NxtPageData = $NextPageRequest.Value
$NextPageUri = $NextPageRequest."@odata.nextLink"
$ResultsValue = $ResultsValue + $NxtPageData
}
}
##Return completed results
return $ResultsValue
}
function Report-ConditionalAccess{
<#
.SYNOPSIS
Returns a report of Conditional Access Policies in a tenent
#>
# Application (client) ID, tenant ID and secret
Param(
[parameter(Mandatory = $true)]
$clientId,
[parameter(Mandatory = $true)]
$tenantId ,
[parameter(Mandatory = $true)]
$clientSecret
)
$apiUri = "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies"
$token = GetGraphToken -clientId $clientId -tenantId $tenantId -clientSecret $clientSecret
$Policies = RunQueryandEnumerateResults -apiuri $apiUri -token $token
foreach($policy in $policies){
$policy | convertto-json | out-file ("$($policy.displayName).json").replace('[','').replace(']','').replace('/','')
}
}
|
Since we want this to run on a schedule, here’s my pipeline.
You can choose not to upload the files to the Blob Container. Simply remove the last step from the yml file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# You can edit the cron below to change the recurrency.
schedules:
- cron: "0 0 * * FRI"
displayName: Weekly Backup
branches:
include:
- main
stages :
- stage: BackUpPolicies
variables:
- group: 'CA Policies'
jobs:
- deployment: bkp_policies
pool:
vmImage: 'ubuntu-latest'
continueOnError: false
environment: 'AZ Policies'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: PowerShell@2
displayName: 'Back-up AZ CA Policies'
inputs:
filePath: '$(System.DefaultWorkingDirectory)/export_ca_.ps1'
arguments: -clientId $clientid -tenantId $tenantid -clientSecret $ClientSecret
pwsh: true
- task: AzurePowerShell@5
inputs:
azureSubscription: '' #Enter your service connection name here
azurePowerShellVersion: LatestVersion
pwsh: true
ScriptType: 'InlineScript'
Inline: | #Add your storage account name below
$StorageContext = New-AzStorageContext -StorageAccountName <str account name> -StorageAccountKey $(strkey)
Get-ChildItem -File -Recurse -Filter "*.json" | Set-AzStorageBlobContent -Container "ca-policies" -Context $StorageContext -Force
|
Every Friday at midnight this pipeline will automatically back up your policies.