I’m often getting asked – “Why do we need to manage stuff as a Code anyways” and my reply is usually “Well, try implementing conditional access manually with the Azure GUI and see how many mistakes you make.”
Managing CA as a Code not only reduces the human errors, but ensures that we have traceability over who does what in CA and more importantly who did what in the past. It also ensures faster deployments – like, much faster ๐
Once the initial setup is done, it is also very easy to onboard new customers on it.
Many thanks to my colleagues, who inspired me for the solution:
- Roger Zander’s work https://rzander.azurewebsites.net/ . Roger is the Author of the Get-AuthToken function.
- Martin Wรผtrich’s work https://blog.hosebei.ch/ . Martin created a brilliant similar solution. I am sure his code is much more optimized ๐
- Alex Filipin’s work : https://github.com/AlexFilipin/ – he has posted the Policy Repository
So, without further ado, here is what I’m using with success in many productive environments, so this setup is super battle-tested
First a little bit over the setup. You’d need the following components:
- Azure DevOps Org
- A project in DevOps
- A minimum of Entra ID P1 License
- Disabled Security Defaults in Entra ID (So that we can create CA policies)
- Repo in DevOps
- App Registration per client tenant (Also possible with service connection – not in scope of the blog)
This is a diagram of how the whole setup looks like:

Let’s explain:
- Each Conditional Access policy creates 2 groups – One for Temporary Exclusions and One for Permanent Exclusions. The groups are then excluded from the policy
- Each Policy has an Exclusion for the Emergency Access Accounts EXCEPT the policy 110 – Admin protection – All apps Require Strong Auth For BreakGlassAccounts, which ONLY has the Breaking Glass accounts inside
Let’s start with the App Registration in DevOps. You’d need the following Application permissions:
- Application.Read.All
- Directory.ReadWrite.All
- Group.Create
- Group.ReadWrite.All
- GroupMember.Read.All
- Policy.Read.All
- Policy.ReadWrite.ConditionalAccess

Once this is ready, create a Client Secret (Or use a certificate or service connection)
Once this is done, go to DevOps and create a new Variable Group:
You’d need 3 values here:
- Tenant ID (You can find it in the app Registration Overview)
- Application ID (Again in the app registration overview)
- Client Secret – make sure to copy it when you create it as afterwards it is hidden

The next step is to create the Repo.
Here are all files you need: https://github.com/Nikolay-Marinov/Conditional-Access-as-a-Code
Create a new Repo and Upload the files inside:

Now create a simple YAML file:
trigger:
- main
pool:
vmImage: windows-latest
steps:
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Pipeline.Workspace)/s/'
ArtifactName: 'ConditionalAccessPolicies'
publishLocation: 'Container'
Now Upload the other files from the GitHub Repo to the DevOps Repo:
https://github.com/Nikolay-Marinov/Conditional-Access-as-a-Code
Let’s also explain the config file. If you take a look at the release script, you’d notice than it needs 4 parameters:
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
$tenantid, #This one we have from the target tenant
[Parameter(Mandatory = $false)]
$applicationid,#This one we have from the target tenant app ID
[Parameter(Mandatory = $false)]
$clientsecret,#This one we have from the target tenant app cl secret
[Parameter(Mandatory = $false)]
$configname #This one we need to create
)
Here is how – it’s super easy
{
"Policies": [
{ "Name": "801","Status": "Disabled"},
{ "Name": "802","Status": "Disabled"}
],
"Prefix" : "configroar-demo",
"Administrator Group" : "configroar-Demo-CA-Administrators",
"DeployToPilotFirst" : "true"
}
The config is in JSON format and you need one per target tenant
Policies you need to choose how the policies are to be created – in my example they are disabled and I’d recommend to start with disabled ones
The Prefix means how the groups , which are created, will be called – they all start with “ConfigRoar-Demo” in this case
The “Administrator Group” is relevant for the 100-199 policies for Admin protection. They are usually role-targeted but you may want to add more accounts in the scope and this group is for that
and finally “DeployToPilotFirst” is a Boolean Switch , which defines if the policies are to be deployed to a Pilot Group first instead of all Users. The Group will be automatically created and I advise to start with this option
Once you are ready with the “Cocktail” of policies, you can make the release pipeline:
Use the published Artefact from the Build Pipeline and just add one PowerShell Job, where you call the script with the necessary parameters (As you defined them in the Variables Group – DO NOT FORGET TO ADD THE VARIABLES GROUP to the Release Pipeline ๐


That’s it – let’s explore what happens in Azure on the target Tenant in Part 2 of the series