Configuration as Code for Microsoft Intune

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Community Hub.

By Dave Randall – Sr Product Manager and Nina Desnica - Product Manager 2 | Microsoft Intune

 

Microsoft wants to help IT pros do more with less. This sounds great, but how can you put it into action? For Microsoft Intune, we can apply the principles and practices of Configuration as Code. Configuration as Code is the process of applying standardized software development best practices to manage and deploy specific configurations or settings for an application. When done correctly, Configuration as Code helps you:

  • Maintain a secure repository of configurations that represent both current and historical state.
  • Manage configurations and settings across multiple tenants.
  • Automate the deployment of configurations and settings.

 

In this blog, we cover step-by-step Configuration as Code procedures for two Intune tenants. There are many ways to implement Configuration as Code but, for this blog, we specifically focus on:

  1. Configuring and using an Azure DevOps CI/CD pipeline to manage your changes. Learn more about pipelines here: What is Azure Pipelines? - Azure Pipelines.
  2. Demonstrating how to automate a change using two Intune tenants.

 

For additional background on the concepts, tools and technology used and to see this in action, watch the Technical Takeoff session: “Configuration as Code.”

 

Prerequisites

To perform this walkthrough, you’ll need:

 

We’re using the latest versions of these services at the time of this blog post. For simplicity, all steps in this blog use a Windows operating system. The walkthrough also assumes that you’re using tenants used for testing or validation of new processes. As you refine your own Configuration as Code process, you’ll be able to apply the process and principles to your production tenants.

 

Intune Requirements

  1. You’ll need two Intune tenants. For our example, the first is our development tenant where we make and verify changes. We’ll refer to this as “DEV.” The second is our production tenant where we’ll apply an approved change. We’ll refer to this as “PROD.” You can easily create trial tenants to test with a 30-Day Intune trial tenant.
  2. You’ll need a compliance policy that can be used to demonstrate the change managed through the CI/CD pipeline. We’ll create the policy in a later step in our DEV environment.

 

Optional Requirements

  • Install Visual Studio (VS) Code or a code editor of your choice.
  • We recommend that you use Azure key vault to store any credentials used during your Configuration as Code pipeline deployment. You’ll need an Azure Subscription for Azure key vault.

 

Overall Flow

Our demonstration Configuration as Code flow uses the following steps:

Intune_Support_Team_2-1671589425006.png

Preparation Steps

Azure DevOps can manage changes between your DEV and PROD tenants, but first you need to grant the appropriate permissions to Azure DevOps in your Intune tenants. These permissions allow Azure DevOps to access your tenants without an interactive login. Because the Azure DevOps pipeline runs as a service, there is no ability to have Azure DevOps log in interactively to your tenants.

 

Application Registration for user-less access

To achieve user-less access, you need to register an application to call Microsoft Graph with its own identity, i.e. app auth, and not on behalf of a user, i.e. delegated auth. Review this link for additional information about authentication and app registration. Ensure you complete the registration steps and grant Graph permissions to the new application for both DEV and PROD tenants:

 

  1. Register the application

    Sign into the Azure portal using an account with administrator permission. You must use an account in the same Microsoft 365 subscription (tenant) as you intend to register the app with. Repeat this process for both your DEV and PROD tenants.

    Select Azure Active Directory in the left navigation panel, then select App registrations under Manage.


    Config-as-Code-1.png

    To create a new app registration, click on + New registration, fill in the required fields, and click Register.

    You can select “Accounts in this organizational directory only.”  This application will only be used by this tenant.  Multi-tenant applications would be appropriate if you were offering this same application to multiple customers, which is not the case here.

     

    For the Redirect URI, choose “Web” for platform and enter a .

     

    Config-as-Code-2.png

    The new application registration is created. It’s important to save the Application (Client) ID value and the Directory Tenant ID in a secure location.  You will use these later for your Azure DevOps pipeline configuration.

    Config-as-Code-3.png

  2.  Add Graph API Permissions

    To add Graph API permissions, select the API permissions tab, select + Add a permission, and then Microsoft Graph.

    Config-as-Code-4.png

    When Requesting API permissions, make sure to choose Application permissions.

    Add just enough permissions for the resource you’re automating. In this example, we chose to automate creating a Compliance policy, which requires adding permissions for 'DeviceManagement.Configuration'.
    Because we’re creating policies, choose 'DeviceManagement.Configuration.ReadWrite.All'

    Config-as-Code-5.png

  3.  Get Administrator Consent

    Next step is to get admin consent for added permissions. Click on Grant admin consent.

    Config-as-Code-6.png


    Config-as-Code-7.png

  4. Create application secret

    Lastly, go to Certificates & secrets to create a New client secret to obtain an authentication token.


    Config-as-Code-8.png

    Config-as-Code-9.png

    Copy the Client secret Value and save it in a secure location.

    After completing app registrations and creating the client secret for DEV and PROD tenants, you need to save the following values in a secure location for the next steps:


    Description

    DEV Tenant

    PROD Tenant

    Tenant ID

    tenantId_DEV

    tenantId_PROD

    Application (Client) ID

    appId_DEV

    appId_PROD

    App (Client) Secret Value

    appSecret_DEV

    appSecret_PROD

Preparation Steps in Azure DevOps

  1. Create a new project and Repository.

    Go to Azure DevOps and click on + New project, to create an Azure DevOps Project. Add a project name and description and then click Create. This will automatically create a default repository with the same name as the project in this example, “Configuration as Code.”


    Config-as-Code-10.png

  2. Clone the code to your local computer.

    If you don’t already have it, you can install Git from the Git downloads page. Or from a Command prompt, (Click Start > Command Prompt to open a new command window) you can use the winget command:

    winget install --id Git.Git -e --source winget

     

    More information about winget is here.


    Config-as-Code-11.png

    Once installed, Click Start > Command Prompt to open a new command window.

    Then, make a folder “ConfigAsCode” by typing:

    MD ConfigAsCode <enter>


    Config-as-Code-12.png

    Change to that folder by typing:

    CD ConfigAsCode <enter>


    and issue the git clone command. You can use the URL copied from the “HTTPS” URL shown in the “Clone to your computer” section of your Repo in Azure DevOps.  Replace {instance_name} with your own instance name in the example below.


    For example:

    git clone https://{instance_name}.azure.com/david0828/David_Config_as_Code/_git/David_Config_as_Code David_Config_as_Code.

    This creates a new folder “David_Config_as_Code” under your ConfigAsCode folder.


    Click on the repo Configuration as Code, clone the code to your computer, and initialize the main branch with README.

    NOTE: You can also work directly from the browser editor.

    Config-as-Code-13.png

  3. Create a folder/file structure in the repository
    In the default “Configuration as Code” repository, create a new folder, “intune-devicecompliance.” You’ll need to create both a folder and a file at the same time since you’re not able to create just an empty folder.

    Config-as-Code-14.png

    Azure DevOps requires that you add a file if you’ve sync’d with git. You can create a placeholder file called “test” to get the folder created.

    Config-as-Code-15.png

    This folder will later have a structure that looks like the image below.

    Config-as-Code-16.png

  4. Add a PowerShell script to repository.

    Add a PowerShell script, “CompliancePolicy_Export_Import.ps1,” that will automate exporting all compliance policies from the DEV tenant and importing them into PROD tenant. This script will be automated and run with app-only permissions (non-interactive) as we prepare an app that will have the right permissions to run. You can download the script sample directly from the GitHub PowerShell Intune samples and modify it to fit your needs.


    To set up JIT registration for ADE on the admin side, refer to the following information.

     

    Important: Microsoft does not support the scripts themselves, even if they are on our GitHub repository. They are provided for example only. You are responsible for anything that they may do within your environment. Always test!

     

    The PowerShell example, CompliancePolicy_Add.ps1, is simply a script that creates a compliance policy with some static values. You’re welcome to modify that script to accommodate any type of compliance policy you wish to generate. The same repository has an export compliance policy script which exports a JSON file containing the policy information. By combining those two scripts, you can export a policy JSON file from your DEV environment, remove any read-only properties from the JSON body, and then import it into your PROD environment. For the purposes of this blog, we’ll import a new compliance policy in the PROD tenant.

     

    If you have downloaded the CompliancePolicy_Add.ps1 file, you need to make a few changes to the script to change it to userless authentication. First, start by removing the entire “Get-AuthToken” function. It should be lines 12-149. Paste in the function from this blog, in the Authentication section, starting at line 12. Proceed with removing additional references to the “Get-AuthToken” function. It should be lines 260-308.

    You will also need to make a change to the script to match the variables you will define in your Variable Group later in this section. Use the following PowerShell snippet and insert at the beginning of the script.

    param( [Parameter(Mandatory=$True)] [string]$appId_DEV, [Parameter(Mandatory=$True)] [string]$appSecret_DEV, [Parameter(Mandatory=$True)] [string]$tenantId_DEV )
    Authentication

    Use the following authentication function PowerShell snippet. It uses ClientID and AppSecret for the policy configuration:

    # Add environment variables to be used by Connect-MgGraph. $Env:AZURE_CLIENT_ID = $appId_DEV #application id of the client app $Env:AZURE_TENANT_ID = $tenantId_DEV #Id of your tenant $Env:AZURE_CLIENT_SECRET = $appSecret_DEV #secret of the client app # Tell Connect-MgGraph to use your environment variables. Connect-MgGraph -EnvironmentVariable​
    Note: To use these commands, you will need to update Microsoft.Graph.Authentication module to V2 as described here. Also, have a look at Microsoft Graph PowerShell V2 Changelog and Upgrade Guide to better understand Environment Variable Based Auth. Use the following PowerShell snippet to install the needed modules:
    Install-Module Microsoft.Graph -AllowPrerelease -AllowClobber -Force


    Use Invoke-MgGraphRequest
    Replace the line 234 in this file CompliancePolicy_Add.ps1:

    Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

     

    with the following PowerShell snippet:

    Invoke-MgGraphRequest -Uri $uri -Method Post -Body $JSON

     

  5. Create a variable group in the pipeline library to store variables.
    You don’t want to keep your secrets in the code as strings. There are two methods to store secrets–as part of the library or in Azure key vault. The more secure and recommended option is to use Azure key vault, which is tied to an Azure subscription. For our example, we’ll store the secrets in a variable group in the pipeline library. Ensure that you review the secret storage requirements with your security team. If you are going to use Azure Key vault to store your secrets, skip ahead to step 5.2.
    1. Store variables in a variable group in Azure DevOps library.

      Go to Pipelines, then Library, and then select + Variable group. Add the variable group name and description. Then click +Add to add each of the variables and provide a Name and Value for each.

      Config-as-Code-17.png


      Click the padlock icon Intune_Support_Team_0-1671588656927.png to change the variable type from “string” to “secret.”


      Config-as-Code-18.png

      Continue adding all variables that you saved in the previous step Create Application secret.


      DEV Tenant

      PROD Tenant

      tenantId_DEV

      tenantId_PROD

      appId_DEV

      appId_PROD

      appSecret_DEV

      appSecret_PROD


      Config-as-Code-19.png

      If you’ve followed the steps in 5.1, you can skip ahead to step 6.

    2.  Store variables to Azure Key Vault.

      1. Create the Azure key vault by following Quickstart - Create an Azure key vault with the Azure portal.

        Config-as-Code-20.png

        Add secrets to the Azure key vault. In this example, we will need tenantId, appSecret, and appId.

        Config-as-Code-21.png

      2. Link the secrets from an Azure key vault in the Azure DevOps library.
        Configure linking secrets from an Azure key vault by following the instructions in the Link secrets from an Azure key vault section of Add and use variable groups.

        Config-as-Code-22.png

        After populating Azure subscription and Key vault name that you created in previous step, you will be able to choose secrets that you created in Azure key vault. After you select all of them, save this variable group.

        Config-as-Code-23.png

  6. Create Pipeline as code in the repository.

    Azure Pipeline automatically builds and tests code projects. Azure Pipelines combine continuous integration (CI) and continuous delivery (CD) to test and build your code and ship it to any target. You accomplish this by defining a pipeline. The latest way to build pipelines is with the YAML pipeline editor.

    In the folder “intune-devicecompliance” create a new file and save as .yml. That will be your simple pipeline as code deploymentpipeline.yml.Thepurpose of this file is to indicate what steps must be executed when the pipeline is deployed. For example, the deploymentpipeline.yml will run the PowerShell script CompliancePolicy_Export_Import.ps1.

    Note: Since you defined all needed variables in a variable group in Pipeline library, you can simply reference the variable group “configuration-as-code,” which will allow you to use all variables that are part of the group: appId_DEV, appSecret_DEV, tenantId_DEV, appId_PROD, appSecret_PROD, and tenantId_PROD.

     

    Intune_Support_Team_1-1671589030970.png

     

  7.  Create a Pipeline deployment.

    Create a pipeline deployment from an existing YAML file that we created in the previous step. This triggers Azure DevOps to run the script specified in the deploymentpipeline.yml file. Go to Pipelines, click on Create pipeline, and then select Azure Repos Git (YAML). Select the repository where you stored the YAML file, Configuration as Code, select Existing Azure Pipelines YAML file, find a path to the deploymentpipeline.yml file, and then click Continue. Review your pipeline and click Run to deploy it.

    Config-as-Code-24.png

    After the pipeline is complete, you will see a successful deployment. Click on Jobs to see more information about the steps and execution.

    Config-as-Code-25.png

    If the pipeline fails, you’ll need to check the error information to troubleshoot and fix the problem.

 

Other considerations

Tenant specific identifiers

When performing CREATE operations, ensure that your JSON request body does not include auto-generated, read-only properties. For example, when objects such as a policy or an application are created, Intune assigns that object a unique identifier (typically ‘id’). These identifiers are both globally unique and unique to a tenant. Exporting a policy from DEV tenant includes the object identifier and other read-only properties, such as createdDateTime. Using the JSON output directly from the export process as input to the PROD tenant fails because the identifier and other read-only properties can’t be included during the import/creation process. This is because Intune automatically assigns an identifier for the newly created objects and sets the read-only properties at the time of creation. Therefore, some special handling of properties is required. You can see an example of this property removal in the PowerShell sample script, Import policy JSON. Look specifically at line 328 (copied here for convenience).

 

 

$JSON_Convert = $JSON_Data | ConvertFrom-Json | Select-Object -Property * -ExcludeProperty id,createdDateTime,lastModifiedDateTime,version

 

 

 

Here you can see that the fields that are auto-generated by Intune are excluded from the import process

 

Intune assignments

Intune assignments are the objects that bind a policy or application and your users/devices. When created, assignments tell Intune to apply the policy or application to the users or devices in the assignment. Just like exporting a policy or application, exporting an assignment will include a unique identifier that will need to be removed before using the assignment as input to CREATE a new assignment in a different tenant.

 

More importantly, for assignments, Azure AD security group identifiers are unique between tenants. When transferring an assignment from your DEV to PROD tenant, your code will have to specifically determine the appropriate Azure AD security group identifier in the PROD tenant before creating the assignment. You cannot use the Azure AD security group identifier that was in the DEV tenant. For the purposes of simplicity, this blog post does not discuss creating assignments.

 

Microsoft 365 DSC

This Microsoft solution allows you to automate configuration changes, take configuration snapshots, monitor and auto correct for configuration for drift, and compare configurations between two tenants. This solution adopts many of the Configuration as Code principles. You can view additional information here in Microsoft365 DSC.

 

We hope you found this guide helpful and let us know how it goes if you try it out! If you have any questions or suggestions for improvements to this blog post, leave a comment below or reach out to us on Twitter @IntuneSuppTeam.

Leave a Reply

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

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.