Skip to content
Manddje edited this page Apr 19, 2024 · 16 revisions

Getting Started

Entra ID App Registration permissions

Before running IntuneCD create an app registration in Entra ID

After creating the App registration -> browse to API permissions -> Click Add Permission -> Click the Microsoft Graph -> Application permissions.

Select each of the following permissions:

To access Intune data:

  • DeviceManagementApps.ReadWrite.All
  • DeviceManagementConfiguration.ReadWrite.All
  • DeviceManagementServiceConfig.ReadWrite.All
  • DeviceManagementManagedDevices.ReadWrite.All
  • DeviceManagementRBAC.ReadWrite.All
  • Group.Read.All
  • Policy.Read.All
  • Policy.ReadWrite.ConditionalAccess
  • Application.Read.All

To access Entra data:

  • Domain.ReadWrite.All
  • Policy.ReadAll
  • Policy.ReadWrite.AuthenticationFlows
  • Policy.ReadWrite.AuthenticationMethod
  • Policy.ReadWrite.Authorization
  • Policy.ReadWrite.DeviceConfiguration
  • Policy.ReadWrite.ExternalIdentities
  • Policy.ReadWrite.SecurityDefaults
  • Group.ReadWrite.All

Make sure to perform all necessary security reviews within your organizations before deploying in production environments

Installing

IntuneCD is built using Python and requires Python version 3.9 or higher to be installed. To install Python on your device, head over to https://python.org

The tool can run on any platform, and does not require you to have any knowledge of Python.

IntuneCD is available on PyPi and can be installed using pip:

pip install IntuneCD

Updating

Updating IntuneCD is also done using pip:

pip install IntuneCD --upgrade

Help

To see what parameters you must provide when using IntuneCD, run the following in your terminal:

IntuneCD-startbackup --help
IntuneCD-startupdate --help
IntuneCD-startdocumentation --help

Example Azure DevOps pipeline

# PIPELINE VARIABLES THAT NEEDS TO BE DEFINED FIRST FOR THIS PIPELINE TO WORK:
# CLIENT_ID (application ID), CLIENT_SECRET (application secret), TENANT_NAME, USER_EMAIL (email that will be used in the commit), USER_NAME (name that will be used in commit)
# based on https://github.com/aaronparker/intune-backup-template

trigger:
  none
schedules:
  - cron: '0 1 * * *'
    displayName: "1am"
    branches:
      include:
      - main
    always: true

jobs:
  - job: backup_document
    displayName: Backup configuration and generate markdown
    pool:
      vmImage: ubuntu-latest
    continueOnError: false
    steps:
    - checkout: self
      persistCredentials: true

    # Set git global settings
    - task: Bash@3
      displayName: Configure Git
      inputs:
        targetType: 'inline'
        script: |
          git config --global user.name $(USER_NAME)
          git config --global user.email $(USER_EMAIL)
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true

    - task: Bash@3
      displayName: Remove existing prod-backup directory
      inputs:
        targetType: 'inline'
        script: |
          rm -rfv "$(Build.SourcesDirectory)/prod-backup"
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

    # Install IntuneCD
    # https://github.com/almenscorner/IntuneCD
    - task: Bash@3
      displayName: Install IntuneCD
      inputs:
        targetType: 'inline'
        script: |
          pip3 install IntuneCD
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true

    # Backup the latest configuration, using the current directory
    - task: Bash@3
      displayName: IntuneCD backup
      inputs:
        targetType: 'inline'
        script: |
          mkdir -p "$(Build.SourcesDirectory)/prod-backup"
          IntuneCD-startbackup \
              --mode=1 \
              --output=json \
              --path="$(Build.SourcesDirectory)/prod-backup"
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true
      env:
        TENANT_NAME: $(TENANT_NAME)
        CLIENT_ID: $(CLIENT_ID)
        CLIENT_SECRET: $(CLIENT_SECRET)

    # Commit changes and push to repo
    - task: Bash@3
      displayName: Commit changes
      name: commitAndsetVariable
      inputs:
        targetType: 'inline'
        script: |
          DATEF=`date +%Y.%m.%d`
          git add --all
          # modified files in folder prod-backup
          var=$(git diff --name-only --staged -- prod-backup)
          echo "##vso[task.setVariable variable=CHANGE_DETECTED;isOutput=true;]$var"
          git commit -m "Intune config backup $DATEF"
          git push origin HEAD:main
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

    # Create markdown documentation
    # Install IntuneCD
    # https://github.com/almenscorner/IntuneCD
    - task: Bash@3
      displayName: Generate markdown document
      inputs:
        targetType: 'inline'
        script: |
          if [ ! -z "$(commitAndsetVariable.CHANGE_DETECTED)" ]
          then
            INTRO="Intune backup and documentation generated at $(Build.Repository.Uri) <img align=\"right\" width=\"96\" height=\"96\" src=\"./logo.png\">"
            IntuneCD-startdocumentation \
                --path="$(Build.SourcesDirectory)/prod-backup" \
                --outpath="$(Build.SourcesDirectory)/prod-as-built.md" \
                --tenantname=$TENANT_NAME \
                --intro="$INTRO" \
                #--split=Y
          else
            echo "no configuration backup change detected in the last commit, documentation will not be created"
          fi
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true
      env:
        TENANT_NAME: $(TENANT_NAME)

    # Commit changes and push to repo
    - task: Bash@3
      displayName: Commit changes
      inputs:
        targetType: 'inline'
        script: |
          DATEF=`date +%Y.%m.%d`
          git add --all
          git commit -m "Intune config as-built $DATEF"
          git push origin HEAD:main
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

  - job: tag
    displayName: Tag repo
    dependsOn: backup_document
    condition: and(succeeded(), ne(dependencies.backup_document.outputs['commitAndsetVariable.CHANGE_DETECTED'], ''))
    pool:
      vmImage: ubuntu-latest
    continueOnError: false
    steps:
    - checkout: self
      persistCredentials: true

    # Set git global settings
    - task: Bash@3
      displayName: Configure Git
      inputs:
        targetType: 'inline'
        script: |
          git config --global user.name $(USER_NAME)
          git config --global user.email $(USER_EMAIL)
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true

    - task: Bash@3
      displayName: Pull origin
      inputs:
        targetType: 'inline'
        script: |
          git pull origin main
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

    - task: PowerShell@2
      displayName: Git tag
      inputs:
        targetType: 'inline'
        script: |
          # change in configuration backup folder detected, create TAG
          $DATEF= Get-Date -format 'yyyy.MM.dd_HH.mm'
          git tag -a "v$DATEF" -m "Microsoft Intune configuration snapshot $DATEF"
          git push origin "v$DATEF" *> $null # even status information goes to stderr :(
        failOnStderr: true
        pwsh: false
        workingDirectory: '$(Build.SourcesDirectory)'

  - job: publish
    displayName: Publish as-built artefacts
    dependsOn: tag
    condition: and(succeeded(), ne(dependencies.backup_document.outputs['commitAndsetVariable.CHANGE_DETECTED'], ''))
    pool:
      vmImage: ubuntu-latest
    continueOnError: false
    steps:
    - checkout: self
      persistCredentials: true

    # Install md-to-pdf
    # https://github.com/simonhaenisch/md-to-pdf
    - task: Bash@3
      displayName: Install md-to-pdf
      inputs:
        targetType: 'inline'
        script: |
          npm i --location=global md-to-pdf
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: true

    # Convert markdown document to HTML
    - task: Bash@3
      displayName: Convert markdown to HTML
      inputs:
        targetType: 'inline'
        script: |
          cat "$(Build.SourcesDirectory)/prod-as-built.md" | md-to-pdf --config-file "$(Build.SourcesDirectory)/md2pdf/htmlconfig.json" --as-html > "$(Build.SourcesDirectory)/prod-as-built.html"
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

    - task: PublishBuildArtifacts@1
      inputs:
        pathToPublish: "$(Build.SourcesDirectory)/prod-as-built.html"
        artifactName: "prod-as-built.html"

    # Convert markdown document to PDF
    - task: Bash@3
      displayName: Convert markdown to PDF
      inputs:
        targetType: 'inline'
        script: |
          cat "$(Build.SourcesDirectory)/prod-as-built.md" | md-to-pdf --config-file "$(Build.SourcesDirectory)/md2pdf/pdfconfig.json" > "$(Build.SourcesDirectory)/prod-as-built.pdf"
        workingDirectory: '$(Build.SourcesDirectory)'
        failOnStderr: false

    - task: PublishBuildArtifacts@1
      inputs:
        pathToPublish: "$(Build.SourcesDirectory)/prod-as-built.pdf"
        artifactName: "prod-as-built.pdf"