Automating with PowerShell: Deploying default Intune configurations

So I’ve been kind of Intune focused lately, mostly because I’m just really enjoying the technology. I also see that a lot of MSPs are still struggling with deploying a default autopilot configuration and are kind of avoiding it with the worries that you make a total mess of things. 🙂

To help, this time I’m giving a little tutorial on how you can use your own endpoint management portal to create a baseline set of deployments you send to all of your managed tenants. Before we do anything, you’ll have to setup the Secure Application Model.

Setting up default configurations at all clients

So we want to make sure all of our new tenants and current tenants use the same configuration for Autopilot related tasks. To do that we first go to the endpoint management portal and we log in. After you log into the portal go to Devices -> Enroll Devices -> Deployment profiles

Now click add, and configure all the options you want to use, but don’t create the profile yet. When you’re at the last page. Hit F12 on your keyboard to open Chrome/Edge/Firefox’s development tools and then click on “Network”.

After clicking on network, feel free to hit the save button in the endpoint management portal. You’ll now see a POST request in the list. Click on this request and scroll down to the bottom where you’ll see “request payload”. Click on view source and copy that entire JSON string, and put it in the script below.

$ApplicationID = "YourAppID"
$applicationsecret = "yourappsecret"
$refreshtoken = "YourRefreshToken"
$MyTenant = "YourPartnertenant.onmicrosoft.com"
$AutopilotProfileName = "Autopilot Default Profile"
$intuneBody = '{"@odata.type":"#microsoft.graph.azureADWindowsAutopilotDeploymentProfile","displayName":"autopilot default profile","description":"This deployment profile has been created by the cyberdrain Deployment script","deviceNameTemplate":"%SERIAL%","language":"os-default","enableWhiteGlove":false,"deviceType":"windowsPc","extractHardwareHash":true,"roleScopeTagIds":[],"hybridAzureADJoinSkipConnectivityCheck":false,"outOfBoxExperienceSettings":{"deviceUsageType":"shared","hideEscapeLink":true,"hidePrivacySettings":true,"hideEULA":true,"userType":"standard","skipKeyboardSelectionPage":true}}'
$IntuneGraphURL = "https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles"
function Connect-graphAPI {
    [CmdletBinding()]
    Param
    (
        [parameter(Position = 0, Mandatory = $false)]
        [ValidateNotNullOrEmpty()][String]$ApplicationId,
        
        [parameter(Position = 1, Mandatory = $false)]
        [ValidateNotNullOrEmpty()][String]$ApplicationSecret,
        
        [parameter(Position = 2, Mandatory = $true)]
        [ValidateNotNullOrEmpty()][String]$TenantID,

        [parameter(Position = 3, Mandatory = $false)]
        [ValidateNotNullOrEmpty()][String]$RefreshToken

    )
    Write-Verbose "Removing old token if it exists"
    $Script:GraphHeader = $null
    Write-Verbose "Logging into Graph API"
    try {
        if ($ApplicationId) {
            Write-Verbose "   using the entered credentials"
            $script:ApplicationId = $ApplicationId
            $script:ApplicationSecret = $ApplicationSecret
            $script:RefreshToken = $RefreshToken
            $AuthBody = @{
                client_id     = $ApplicationId
                client_secret = $ApplicationSecret
                scope         = 'https://graph.microsoft.com/.default'
                refresh_token = $RefreshToken
                grant_type    = "refresh_token"
               
            }
            
        }
        else {
            Write-Verbose "   using the cached credentials"
            $AuthBody = @{
                client_id     = $script:ApplicationId
                client_secret = $Script:ApplicationSecret
                scope         = 'https://graph.microsoft.com/.default'
                refresh_token = $script:RefreshToken
                grant_type    = "refresh_token"
               
            }
        }
        $AccessToken = (Invoke-RestMethod -Method post -Uri "https://login.microsoftonline.com/$($tenantid)/oauth2/v2.0/token" -Body $Authbody -ErrorAction Stop).access_token

        $Script:GraphHeader = @{ Authorization = "Bearer $($AccessToken)" }
    }
    catch {
        write-error "Could not log into the Graph API for tenant $($TenantID): $($_.Exception.Message)"
    }

}

write-host "Generating token to log into Intune" -ForegroundColor Green
Connect-graphAPI -ApplicationId $applicationid -ApplicationSecret $applicationsecret -RefreshToken $refreshtoken -TenantID $MyTenant
$Tenants = (Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/contracts?`$top=999" -Method GET -Headers $script:GraphHeader).value
foreach ($Tenant in $Tenants) {
    Connect-graphAPI -ApplicationId $applicationid -ApplicationSecret $applicationsecret -RefreshToken $refreshtoken -TenantID $($tenant.customerid)
    try {
        $Existing = (Invoke-RestMethod -Uri "$($IntuneGraphURL)" -Headers $Script:GraphHeader -Method GET -ContentType "application/json").value | Where-Object { $_.displayname -eq $AutopilotProfileName }
        if ($Existing) { 
            write-host "Removing old profiles for $($tenant.defaultDomainName)"
            $Existing | ForEach-Object { 
                Invoke-RestMethod -Method DELETE -Uri "$($IntuneGraphURL)/$($_.id)/assignments" -UseBasicParsing -ContentType "application/json" -ErrorAction SilentlyContinue -Headers $Script:GraphHeader 
                start-sleep 1
                Invoke-RestMethod -Method DELETE -Uri "$($IntuneGraphURL)/$($_.id)" -UseBasicParsing -ContentType "application/json" -ErrorAction SilentlyContinue -Headers $Script:GraphHeader
            }
        }
        write-host "Creating Deployment Profile for $($tenant.defaultdomainname)" -ForegroundColor Green
        write-host "Assigning to all devices"
        start-sleep 1
        $AssignID = Invoke-RestMethod -Uri "$($IntuneGraphURL)" -Headers $Script:GraphHeader -body $intuneBody -Method POST -ContentType "application/json"
        write-host "Created the profile with ID $($AssignID.id) for tenant $($tenant.customerId)"
        #$AssignBody = '{"target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"}}'
        #Invoke-RestMethod -uri "$($IntuneGraphURL)/$($AssignID.ID)/assignments" -Headers $Script:GraphHeader -body $AssignBody -Method POST -ContentType "application/json"

Now you can actually use this same script for any other Intune request. All you have to do is change the URL, and change the JSON body to what you want to setup. This way you can easily create a office configuration profile, or a policy you want to set per default at all your clients. At our MSP we do the following, which are some ideas you might want to use too;

  • Configure Autopilot
  • Configure Onedrive KFM
  • Configure outlook autoconfiguration silently
  • Configure the user ESP
  • Install our RMM
  • Install the correct office suite

I’ve commented the last two lines. You can remove this comment to also immediately assign the profile to all devices. I suggest running this script on a schedule via an Azure Function or Azure automation so all clients always have the exact same configuration.

As always, Happy PowerShelling!

2 Comments

  1. Bill September 21, 2021 at 3:25 pm

    That’s a handy script for Intune CyberDrain. Thank you for sharing.

Leave a comment

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.