Menu Close

Backup and restore Conditional access policies with PowerShell

Since the summer of 2020, the AzureAD PowerShell module provides cmdlets that can help you manage Conditional access policies. This can give you some nice options to backup, document and restore Conditional access policies. In my opinion, the PowerShell cmdlets aren’t all that intuitive, which is the reason I want to show in this post how to backup and restore Conditional Access Policies with PowerShell.

AzureAD module or the Graph API?

The cmdlets we are going to use today are part of the AzureAD module. I assume they are a wrapper around the Graph API, which also makes it possible to manage Conditional Access Policies. You do have the option to call the API directly from PowerShell. (To learn more about how, see  my previous post.)

So which should you choose? Practically, there are some advantages on using the Graph API. The biggest one being that it works natively in PowerShell 7+, where the AzureAD module doesn’t. And automation running in for example Azure Functions is more clean thanks to working with a service principal.
If you would like to use PowerShell 7 and a Service Principal without learning the API, you could consider the DCToolbox module.

In my opinion, using the AzureAD module is the best way to go if you want to call these scripts interactively. Authentication through Connect-AzureAD is a breeze compared to the service principal approach you need for the Graph API. So if that is your use case, the AzureAD module is the way to go.

Prerequisites and limitations

The cmdlets we are going to use are part of the AzureAD PowerShell module. You can install the module from the PowerShell gallery by using

Install-Module AzureAD

After you have installed the module, you can import it and connect to your tenant

Import-Module AzureAD
Connect-AzureAD

An Authentication windows will pop up and you can authenticate as usual.

There are some limitations to keep in mind though:

PowerShell 5.1

AzureAD is officially not supported in PowerShell 7+. You are able to install the module, but you are not able to authenticate. So you have to use Windows PowerShell. There is more information on that here.

You should be able to use the module in PowerShell 7+ by using Windows PowerShell compatibility, but you do need Windows PowerShell on the device.

Import-Module AzureAD -UseWindowsPowerShell

The AzureAD module works natively in Cloud Shell, but the current version does not have the Conditional Access cmdlets available at the time of writing.

Preview policies

When you run the scripts to backup and restore Conditional Access Policies, all policies that are in Preview will not be touched. You do not get warned on this, you will just get an incomplete list.
To work around this, you can use the AzureADPreview module, as this module does collect the policies that are in preview. Read more about it here.

Backup up Conditional Access policies

Enough talking, let’s start working with the policies.
Our first goal is to create a backup for all the policies.

I did some experimenting with this and found the cleanest way to store the policies is by using JSON files. I did try to get a clean overview in a CSV, as that might be helpful as documentation. But there is too much layering in the objects to get a nice view. The JSONs are pretty readable with the right editor (like Visual Studio Code).

With the following code, you can create the backup files. All files will be saved as the Conditional Access ID.

 

Restore Conditional Access policies

While creating the backup was pretty straightforward, it is a bit more work to use those files to create new Conditional Access policies. The reason is that the policy object in PowerShell is divided into pretty specific types. If you use the following code, it will create new policies based on all the policies you just stored in JSON. By using the Prefix parameter, you can rename the policies, for example by adding restore in front of the name.

Remove existing policies

If you run above script, it will create all policies again, even if they already exist. So If you want to overwrite the existing policies, it might be a good idea to first remove the policies that you had backed up. You can do that by using the following code:

Note: There is no check on the Remove-AzureADMSConditionalAccessPolicy cmdlet. Be careful that you do not remove items you wanted to keep

Conclusion

So this is how you can Backup and restore Conditional access policies with PowerShell. I think it is great there is finally automation around the policies available, so you are able to make this part of your infra as code, to ease migrations or to set up a business standard.

11 Comments

  1. Pingback:HOWTO: Get rid of the Conditional Access Baseline Policies in your Azure AD tenant - The things that are better left unspoken

  2. Pingback:Bug in Get-AzureMSConditionalAccessPolicy cmdlet? | F12

  3. Michael

    This worked like a charm for so long up until Friday (2021-04-16) where I’m running into errors that the User and Applicaton cannot be found within the object:

    Anyone know what might’ve changed recently? Thanks!

    Cannot convert value “@{Applications=; Users=; Platforms=; Locations=; SignInRiskLevels=System.Object[];
    ClientAppTypes=System.Object[]}” to type “Microsoft.Open.MSGraph.Model.ConditionalAccessConditionSet”. Error: “Cannot
    convert value “@{IncludeApplications=System.Object[]; ExcludeApplications=System.Object[];
    IncludeUserActions=System.Object[]; IncludeProtectionLevels=}” to type
    “Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationCondition”. Error: “Cannot convert the
    “@{IncludeApplications=System.Object[]; ExcludeApplications=System.Object[]; IncludeUserActions=System.Object[];
    IncludeProtectionLevels=}” value of type “System.Management.Automation.PSCustomObject” to type
    “Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationCondition”.””
    At C:\Restore-ConditionalAccessPoliciesScript.ps1:41 char:5
    + [Microsoft.Open.MSGraph.Model.ConditionalAccessConditionSet]$Cond …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastConstructorException

    The property ‘Users’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\Restore-ConditionalAccessPoliciesScript.ps1:55 char:5
    + $Conditions.Users = $Users
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘Applications’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\Restore-ConditionalAccessPoliciesScript.ps1:66 char:5
    + $Conditions.Applications = $Applications
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    • Michael

      Ah, found a fix. Looks like new backups or exports will contain CreatedDateTime, ModifiedDateTime and a new Devices property. Restoring using the new backup .json files seems to do the trick!

      • Barbara

        So you mean that you can’t restore an old json anymore? That is good to know!
        Thank you for your comment, I will look into the script to check when I find time!

  4. Mavi

    I am trying to put this in AzureAutomation runbook. RunAsAccount/ServicePrinciple is having Conditional Access administrator and API permissions to read/write conditional policies – But still getting this error:
    >>>>>
    Get-AzureADMSConditionalAccessPolicy : Error occurred while executing GetAzureADMSConditionalAccessPolicies
    Code: AccessDenied
    Message: You cannot perform the requested operation, required scopes are missing in the token.
    InnerError:
    RequestId:
    DateTimeStamp:
    HttpStatusCode: Forbidden
    HttpStatusDescription: Forbidden
    HttpResponseStatus: Completed

    At line:21 char:16
    + $AllPolicies = Get-AzureADMSConditionalAccessPolicy
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-AzureADMSConditionalAccessPolicy], ApiException
    + FullyQualifiedErrorId :
    Microsoft.Open.MSGraphV10.Client.ApiException,Microsoft.Open.MSGraphV10.PowerShell.GetAzureADMSConditionalAccessPolicy

  5. John

    I am currently having an issue where the cmdlet only retrieves three of my policies and I have eight. Is there a limitation with the cmlet Get-AzureADMSConditionalAccessPolicy?

    • Barbara

      Hi John,
      Could it be that the policies you do not see are in preview?
      This is a known limitation with using the regular AzureAD module. As a workaround you can try the AzureADPreview module. Read more about it here.

  6. Freddie Mac

    Hi,

    Great scripts!
    But it seems to only half work when importing my CAP’s – Some fail with the error:
    Message: 1061: ‘devices’ condition must specify the device states to include. Did you intend to include ‘all’?

    It seems to only effect policies where there is “no” device state configured within the Conditional Access Policy.
    A policy that has device state configured, do not import. Only the policies without the device state configured are imported again.

    The ones that do import OK, import fine into the same tenant in which they were exported from.
    Which brings me to another question; When importing CAP’s into another tenant, they all fail, the idea (at least for me) is to simplify the creation on CAP’s when creating new MS365 tenants for different clients.

    Do you know of anyway to achieve this?

    Cheers,

    F

Leave a Reply

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