Automation, automation, automation!
Why should you manually set up dozens of groups, policies and other settings when it could be automated?

To automate our Intune setup, Microsoft Graph API is the answer.
And since most of us aren’t developers, we stick to PowerShell as our script language of choice.

You find the reference for this API on
https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0 , but my experience is that it takes a lot of trial and error to figure out the correct syntax and at the moment the API reference are missing some real world examples.

In this post I will share my functions with you.
Some of them are based on the Microsoft Graph API and Intune Powershell samples found at https://github.com/microsoftgraph/powershell-intune-samples

You could also take a look at the PowerShell Module Microsoft.Graph.Intune for easier authentication and more functions added every month.

Authentication

We have to send an auth token with every REST request we send to MS Graph API

This is how you can authenticate to get this auth token:

function Get-AuthToken {

<#
Authentication against Intune tenant
#>

[cmdletbinding()]

param
(
    [Parameter(Mandatory=$true)]
    $User
)

$userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User

$tenant = $userUpn.Host

Write-Host "Looking for AzureAD module..."

    $AadModule = Get-Module -Name "AzureAD" -ListAvailable

    if ($AadModule -eq $null) {

        Write-Host "AzureAD PowerShell was not found - looking for AzureADPreview"
        $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable

    }

    if ($AadModule -eq $null) {
        write-host
        write-host "AzureAD Powershell module not installed..." -f Red
        write-host "Install with 'Install-Module AzureAD' or 'Install-Module AzureADPreview' fra en elevert PowerShell-prompt" -f Yellow
        write-host "Script cannot continue..." -f Red
        write-host
        exit
    }

# Getting path to ActiveDirectory Assemblies
# If the module count is greater than 1 find the latest version

    if($AadModule.count -gt 1){

        $Latest_Version = ($AadModule | select version | Sort-Object)[-1]

        $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version }

            # Checking if there are multiple versions of the same module found

            if($AadModule.count -gt 1){

            $aadModule = $AadModule | select -Unique

            }

        $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
        $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

    }

    else {

        $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
        $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"

    }

[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null

[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null

$clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"

$redirectUri = "urn:ietf:wg:oauth:2.0:oob"

$resourceAppIdURI = "https://graph.microsoft.com"

$authority = "https://login.microsoftonline.com/$Tenant"

    try {

    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority

    # https://msdn.microsoft.com/en-us/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx
    # Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession

    $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"

    $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")

    $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result

        # If the accesstoken is valid then create the authentication header

        if($authResult.AccessToken){

        # Creating header for Authorization token

        $authHeader = @{
            'Content-Type'='application/json'
            'Authorization'="Bearer " + $authResult.AccessToken
            'ExpiresOn'=$authResult.ExpiresOn
            }

        return $authHeader

        }

        else {

        Write-Host
        Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red
        Write-Host
        break

        }

    }

    catch {

    write-host $_.Exception.Message -f Red
    write-host $_.Exception.ItemName -f Red
    write-host
    break

    }

}

write-host

# Checking if authToken exists before running authentication
if($global:authToken){

    # Setting DateTime to Universal time to work in all timezones
    $DateTime = (Get-Date).ToUniversalTime()

    # If the authToken exists checking when it expires
    $TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes

        if($TokenExpires -le 0){

        write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow
        write-host

            # Defining User Principal Name if not present

            if($User -eq $null -or $User -eq ""){

            $User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
            Write-Host

            }

        $global:authToken = Get-AuthToken -User $User

        }
}

# Authentication doesn't exist, calling Get-AuthToken function

else {

    if($User -eq $null -or $User -eq ""){

    $User = Read-Host -Prompt "Login name for Azure AD"
    Write-Host

    }

# Getting the authorization token
$global:authToken = Get-AuthToken -User $User

}

Test JSON

Because we are going to use a lot of JSON, let us make a function to check our JSON syntax

Function Test-JSON(){

<#
Testing if JSON syntax is correct
#>

param (

$JSON

)

    try {

    $TestJSON = ConvertFrom-Json $JSON -ErrorAction Stop
    $validJson = $true

    }

    catch {

    $validJson = $false
    $_.Exception

    }

    if (!$validJson){

    Write-Host "Wrong format in JSON" -f Red
    break

    }

}

Set Intune as MDM authority

We start our configuration by setting Intune as our MDM authority.
For this we create two functions that we use together, one to find our AzureAD Oranization ID and one to set Intune as MDM autohority

Usage: Update-MDMAuth -OrgID Get-OrgID

Function Get-OrgID(){

<#
Finds Organization ID
#>

[cmdletbinding()]

$graphApiVersion = "v1.0"
$emptybody = @"
{
}
"@

try {

        $Resource = "organization"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        $GroupsResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        $OrgID = $GroupsResponse.id
        return $OrgID 

     }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Function Update-MDMAuth(){

<#
Sets Mobile Device Device Management Authority to Intune
#>

[cmdletbinding()]

param
(
    $OrgID
)

$graphApiVersion = "v1.0"
$Resource = "/organization/$OrgID/setMobileDeviceManagementAuthority"
$JSON = @"
{
    
}
"@

    try {

        
        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"
    
        }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host

    }

}

Creating groups

Let us create some groups to use with our Intune policies
We create variables with JSON and then use the command

Add-Group -JSON $JSONvariablehere

JSON variable:

$JSON_Group_Group1 = @"
 {
   "description": "Group for Intune",
   "displayName": "Group1",
   "mailEnabled": false,
   "mailNickname": "Group1",
   "securityEnabled": true
 }
 "@

Function to create groups:

Function Add-Group(){

<#
Adding group
#>

[cmdletbinding()]

param
(
    $JSON
)

$graphApiVersion = "v1.0"
$Resource = "groups"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "No JSON specified..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Branding

Let us add some branding.
Like with the groups we add the information in JSON and then run our function to import the settings.

Set-IntuneBrand -JSON $JSON_Branding

JSON:

$JSON_Branding = @"
{
    "intuneBrand":{
    "displayName": "Contoso Inc",
    "contactITName": "Contoso helpdesk",
    "contactITPhoneNumber": "+1555555555",
    "contactITEmailAddress": "helpdesk@contoso.com",
    "contactITNotes": "",
    "privacyUrl": "http://contoso.com/privacy",
    "onlineSupportSiteUrl": "http://contoso.com/support/",
    "onlineSupportSiteName": "Contoso Helpdesk",
    "themeColor": {"r":0,"g":0,"b":255},
    "showLogo": true,
    "showNameNextToLogo": true,
    "@odata.type":"#microsoft.management.services.api.intuneBrand"
    }
}
"@

Function to add branding:

Function Set-IntuneBrand(){


[cmdletbinding()]

param
(
    $JSON
)

$graphApiVersion = "v1.0"
$App_resource = "deviceManagement"

    try {

        if(!$JSON){

        write-host "No JSON was passed to the function, provide a JSON variable" -f Red
        break

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($App_resource)"
        Invoke-RestMethod -Uri $uri -Method Patch -ContentType "application/json" -Body $JSON -Headers $authToken

        }

    }

    catch {

    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Windows Hello configuration

Let us do the configuration of Windows Hello from Enrollment Settings in Intune. By now, you now the drill. Create JSON, import via Microsoft Graph API

Usage: Update-deviceEnrollmentConfiguration -JSON $JSON_Enrollment_Hello -EnConfDesc “This is the default Windows Hello for Business configuration applied with the lowest priority to all users regardless of group membership.”

$JSON_Enrollment_Hello = @"
{
  "@odata.type": "#microsoft.graph.deviceEnrollmentWindowsHelloForBusinessConfiguration",
  "pinMinimumLength": 4,
  "pinMaximumLength": 127,
  "pinUppercaseCharactersUsage": "disallowed",
  "pinLowercaseCharactersUsage": "disallowed",
  "pinSpecialCharactersUsage": "disallowed",
  "state": "enabled",
  "securityDeviceRequired": false,
  "unlockWithBiometricsEnabled": true,
  "remotePassportEnabled": true,
  "pinPreviousBlockCount": 0,
  "pinExpirationInDays": 0,
  "enhancedBiometricsState": "notConfigured"
}
"@

Function Update-deviceEnrollmentConfiguration(){

<#
Update an Enrollment Configuration
#>

[cmdletbinding()]

param
(
    $JSON,
    $EnConfDesc
)

$ConfID = Get-EnrollmentConfID -Desc "$EnConfDesc"
$Resource = "deviceManagement/deviceEnrollmentConfigurations/$ConfID"
$graphApiVersion = "v1.0"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "Ingen JSON spesifisert..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Patch -Body $JSON -ContentType "application/json"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Notifications for Compliance Policy

We create an email template to be added to our compliance policies later.
The template are created in, you guessed it, JSON! We add it with three functions.

Add-Notification -JSON $JSON_Notification_Compliance
$NotificationID = Get-NotificationID -NotificationName “Notification”
Add-NotificationMessage -JSON $JSON_Notification_Compliance_en -NotificationID $NotificationID

$JSON_Notification_Compliance = @"
    {
    "displayName": "Notification",
    "brandingOptions": "includeCompanyLogo, includeCompanyName"
    }
"@

$JSON_Notification_Compliance_no = @"
  {
  "locale": "en-us",
  "subject": "Regarding your mobile device",
  "messageTemplate": "One of your mobile devices are not in compliance and you will be blocked from accessing company reources in 24 hours. 
"
  }
"@

Function Add-Notification(){

<#
Adding template for notifications. Add multiple languages with Get-NotificationId og Add-NotificationMessage
#>

[cmdletbinding()]

param
(
    $JSON
)

$graphApiVersion = "v1.0"
$Resource = "deviceManagement/notificationMessageTemplates"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "Ingen JSON spesifisert..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        $body = [System.Text.Encoding]::UTF8.GetBytes($JSON);
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $body -ContentType "application/json; charset=utf-8"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

####################################################

Function Get-NotificationID(){

<#
Finds Notification ID
#>

[cmdletbinding()]

param
(
    $NotificationName
)

$graphApiVersion = "v1.0"
$emptybody = @"
{
}
"@

try {

        $Resource = "deviceManagement/notificationMessageTemplates?`$filter=displayName+eq+'$NotificationName'"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        $NotificationsResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        $NotificationID = $NotificationsResponse.id
        return $NotificationID 

     }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

####################################################

Function Add-NotificationMessage(){

<#
Adding template. Could be added in multiple languages
#>

[cmdletbinding()]

param
(
    $JSON,
    $NotificationID
)

$graphApiVersion = "v1.0"
$Resource = "deviceManagement/notificationMessageTemplates/$NotificationID/localizedNotificationMessages"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "No JSON specified..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Creting compliance policies

Next up, we have a tricky one, compliance policies.
To get the JSON (our now dear friend) correct for this one, I suggest setting the policies via GUI and then run GET https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicies via Graph Explorer, your best friend while working with this API

For every platform you need JSON like this (if someone know how to add the notification email action together with the noncompliant action, please leave a comment):

$JSON_CompliancePolicy_Android = @"
    {
    "@odata.type": "microsoft.graph.androidCompliancePolicy",
    "displayName": "Android",
    "passwordRequired": true,
    "passwordMinimumLength": 6,
    "passwordRequiredType": "numericComplex",
    "passwordMinutesOfInactivityBeforeLock": 10,
    "storageRequireEncryption": true,
    "scheduledActionsForRule":[{"ruleName":"Mark device noncompliaant","scheduledActionConfigurations":[{"actionType":"block","gracePeriodHours":72,"notificationTemplateId":""}]}]
    }
"@

We then add the policies with the following function.
Usage: Add-DeviceCompliancePolicy -JSON $JSON_CompliancePolicy_Android -graphApiVersion “v1.0”

Function Add-DeviceCompliancePolicy(){

<#
Adding a Device Policy
#>

[cmdletbinding()]

param
(
    $JSON,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceCompliancePolicies"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "No JSON specified..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Assign compliance policy to group

We then assign the policy to a group with this three functions.
Get-GroupID to find the ID of a group by its name, Get-DeviceCompliancePolicyID to find the ID of the policy by name and at last Add-DeviceCompliancePolicyAssignment to bring it together.

You use them like this:

$GroupID = Get-GroupID -GroupName “Group1”
$PolicyID = Get-DeviceCompliancePolicyID -Name “Android”
Add-DeviceCompliancePolicyAssignment -CompliancePolicyId $PolicyID -TargetGroupId $GroupID

Function Get-GroupID(){

<#
Finner Objekt-ID på gruppe
#>

[cmdletbinding()]

param
(
    $GroupName
)

$graphApiVersion = "v1.0"
$emptybody = @"
{
}
"@



    try {

        if($GroupName -eq "" -or $GroupName -eq $null){

        write-host "Ingen gruppenavn spesifisert..." -f Red

        }

        else {

        $Resource = "groups?`$filter=displayName+eq+'$GroupName'"
        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        $GroupsResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        $GroupID = $GroupsResponse.id
        return $GroupID

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Function Get-DeviceCompliancePolicyID(){

<#
Finner ID på DeviceCompliancePolicy
#>

[cmdletbinding()]

param
(
    $Name,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceCompliancePolicies"

    try {

        
        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)?`$filter=displayName+eq+'$Name'"
        $CompliancePolicyResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        $CompliancePolicyID = $CompliancePolicyResponse.id
        return $CompliancePolicyID 

    }

    catch {

    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Function Add-DeviceCompliancePolicyAssignment(){

[cmdletbinding()]

param
(
    $CompliancePolicyId,
    $TargetGroupId,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceCompliancePolicies/$CompliancePolicyId/assign"
    
    try {

        if(!$CompliancePolicyId){

        write-host "Ingen Compliance Policy Id spesifisert" -f Red
        break

        }

        if(!$TargetGroupId){

        write-host "Ingen gruppe-Id spesifisert" -f Red
        break

        }


$JSON = @"
  {
 "assignments": [
 {
 "@odata.type": "#microsoft.graph.deviceCompliancePolicyAssignment",
  "target": {
    "@odata.type": "#microsoft.graph.groupAssignmentTarget",
    "groupId": "$groupid"
  }}
  ]
  }
"@

    $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
    Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

    }
    
    catch {

    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Adding configuration policies

The same thing here as compliance policies. Use Graph Explorer to find the correct JSON like:

$JSON_DevConf_MacOS = @"
    {
    
            "@odata.type": "#microsoft.graph.macOSGeneralDeviceConfiguration",
            "description": "",
            "displayName": "Device restriction - macOS",
            "passwordBlockSimple": true,
            "passwordExpirationDays": 60,
            "passwordMinimumCharacterSetCount": 1,
            "passwordMinimumLength": 8,
            "passwordMinutesOfInactivityBeforeLock": 15,
            "passwordMinutesOfInactivityBeforeScreenTimeout": 10,
            "passwordPreviousPasswordBlockCount": 5,
            "passwordRequiredType": "alphanumeric",
            "passwordRequired": true 
    }
"@

You can also use this for Windows Update policy:

$JSON_DevConf_UpdateForBusiness = @"    
            {
            "@odata.type": "#microsoft.graph.windowsUpdateForBusinessConfiguration",
            "description": "",
            "displayName": "Windows 10 update rings",
            "deliveryOptimizationMode": "httpWithPeeringNat",
            "prereleaseFeatures": "userDefined",
            "automaticUpdateMode": "autoInstallAtMaintenanceTime",
            "microsoftUpdateServiceAllowed": true,
            "driversExcluded": false,
            "installationSchedule": {
                "@odata.type": "#microsoft.graph.windowsUpdateActiveHoursInstall",
                "activeHoursStart": "07:00:00.0000000",
                "activeHoursEnd": "20:00:00.0000000"
            },
            "qualityUpdatesDeferralPeriodInDays": 5,
            "featureUpdatesDeferralPeriodInDays": 30,
            "qualityUpdatesPaused": false,
            "featureUpdatesPaused": false,
            "qualityUpdatesPauseExpiryDateTime": "0001-01-01T00:00:00Z",
            "featureUpdatesPauseExpiryDateTime": "0001-01-01T00:00:00Z",
            "businessReadyUpdatesOnly": "businessReadyOnly"
        }
"@

We then add it to Intune with this function.
Add-DeviceConfiguration -JSON $JSON_DevConf_UpdateForBusiness

Function Add-DeviceConfiguration(){

<#
Adding a Device Configuration
#>

[cmdletbinding()]

param
(
    $JSON,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceConfigurations"

    try {

        if($JSON -eq "" -or $JSON -eq $null){

        write-host "Ingen JSON spesifisert..." -f Red

        }

        else {

        Test-JSON -JSON $JSON

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
        Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

        }

    }

    catch {

    Write-Host
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Assign configuration policy to group

Again we run our Get-GroupID function to get the ID of a group by name.
We then run a function to get the configuration policy ID by name and use the two to assign the policy to a group in the last PowerShell function in this post.

$GroupID = Get-GroupID -GroupName “Group1”
$PolicyID = Get-DeviceConfigurationID -Name “Android”
Add-DeviceConfigurationPolicyAssignment -ConfigurationPolicyId $PolicyID -TargetGroupId $GroupID

Function Get-DeviceConfigurationID(){

<#
Finner ID på DeviceConfigurationPolicy
#>

[cmdletbinding()]

param
(
    $Name,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceConfigurations"

    try {

        $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)?`$filter=displayName+eq+'$Name'"
        $ConfigurationPolicyResponse = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get).value
        $ConfigurationPolicyID = $ConfigurationPolicyResponse.id
        return $ConfigurationPolicyID 
    }

    catch {

    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

Function Add-DeviceConfigurationPolicyAssignment(){

<#
Assigner DeviceConfiguration til gruppe
#>

[cmdletbinding()]

param
(
    $ConfigurationPolicyId,
    $TargetGroupId,
    $graphApiVersion = "v1.0"
)

$Resource = "deviceManagement/deviceConfigurations/$ConfigurationPolicyId/assignments"
    
    try {

        if(!$ConfigurationPolicyId){

        write-host "Ingen Configuration Policy Id spesifiert" -f Red
        break

        }

        if(!$TargetGroupId){

        write-host "Ingen gruppe-Id spesifisert" -f Red
        break

        }


$JSON = @"
 {
  "@odata.type": "#microsoft.graph.deviceConfigurationAssignment",
  "target": {
    "@odata.type": "#microsoft.graph.groupAssignmentTarget",
    "groupId": "$TargetGroupId"
  }}
"@

 

    $uri = "https://graph.microsoft.com/$graphApiVersion/$($Resource)"
    Invoke-RestMethod -Uri $uri -Headers $authToken -Method Post -Body $JSON -ContentType "application/json"

    }
    
    catch {

    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Host "Response content:`n$responseBody" -f Red
    Write-Error "Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    write-host
    break

    }

}

This concludes this first step into the world of Graph API and Intune.