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:
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:
For additional background on the concepts, tools and technology used and to see this in action, watch the Technical Takeoff session: “Configuration as Code.”
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.
Our demonstration Configuration as Code flow uses the following steps:
An illustration of the Configuration as Code flow 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.
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:
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.
A screenshot of the App registrations pane in the Azure Active Directory admin center.
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 .
The Register an application pane in Microsoft Azure.
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. A screenshot the Configuration as Code pane with the Application (Client) ID information highlighted.
Next step is to get admin consent for added permissions. Click on Grant admin consent.A screenshot of the API permissions settings configured for Configuration as Code.
Lastly, go to Certificates & secrets to create a New client secret to obtain an authentication token.
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 |
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.”
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.
A screenshot of a command window with a completed winget command.
Once installed, Click Start > Command Prompt to open a new command window.
Then, make a folder “ConfigAsCode” by typing:
MD ConfigAsCode <enter>
A screenshot of the ConfigAsCode prompts in the Command Prompt.
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.A screenshot of the Configuration as Code pane in Azure DevOps.
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
)
# 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
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
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.A screenshot of the Configuration as Code variables in the Library.
Click the padlock icon to change the variable type from “string” to “secret.”
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 |
If you’ve followed the steps in 5.1, you can skip ahead to step 5.2.
Store variables to Azure Key Vault.
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.A screenshot of options to include with the variable settings when linking secrets to variables.
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.
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.A screenshot of the options for selecting an existing YAML file for the Configure your pipeline setup.
After the pipeline is complete, you will see a successful deployment. Click on Jobs to see more information about the steps and execution. A screenshot of a successful deployment of a pipeline.
If the pipeline fails, you’ll need to check the error information to troubleshoot and fix the problem.
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 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.
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!
Have a couple of minutes to share your feedback? Let us know! https://aka.ms/configuration-as-code-survey.
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.
Post Updates:
01/20/23: updated to include survey link. Thanks for the feedback!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.