Set scope tag by domain of primary user with automation
This automation solves a very small specific use case. As soon as the mobile devices (IOS, Android) are registered in Intune and are not set up via enrollment type profiles, no more scope tags can be set based on device parameters. This is where this automation comes into play.
The script runs regularly and goes through all IOS & Android devices in Intune. There it takes the primary user of the device and resolves the domain to the scope tag via an API. Then the script sets the scope tag in Intune via the API.
Prerequisites
Managed Identity
On the Automation Account you must activate the managed identity. This can be achieved in the settings under "Identity":
Then you can add the managed identity of the Azure Automation Account to the Storage Account with the permissions of "Storage Account Contributor":
App Registration
After that you have to create an App Registration. The process to create and how the variables can be found out, you will find in this tutorial: Get app details and gr... | LNC DOCS (lucanoahcaprez.ch)
The permissions on the App Registration that are necessary for this automation are as follows:
- Device.ReadWrite.All
- User.Read.All
- DeviceManagementConfiguration.ReadWrite.All,
- DeviceManagementRBAC.ReadWrite.All
- DeviceManagementManagedDevices.ReadWrite.All
PowerShell Script
With the following script this automation can be achieved. This is intended to be executed in a runbook. Accordingly, it is important that the variables are added to the runbook and automation account.
Param(
$Device,
$Domain
)
$Success = @()
$NoPM = @()
$NoScopeTag = @()
$NoShortName = @()
$NoOwner = @()
$NoDevice = @()
#Azure Login with Identity
try
{
# "Logging in to Azure..."
Connect-AzAccount -Identity
}
catch {
Write-Error -Message $_.Exception
throw $_.Exception
}
$resourcegroupname = "<yourresourcegroupname>"
$storageaccountname = "<yourstorageaccountname>"
$storagetablename = "<yourstoragetablename>"
#storage account/table connection
$storageaccountkey = (Get-AzStorageAccountKey -ResourceGroupName $resourcegroupname -AccountName $storageaccountname | where {$_.keyname -eq "key1"}).value
$storageContext = New-AzStorageContext -StorageAccountName $storageaccountname -StorageAccountKey $storageaccountkey
$table = Get-AzStorageTable -name $storagetablename -context $storageContext
$hostnames = get-AzTableRow -table $table.CloudTable
#Collect Credential Data for Graph API
$tenantId = Get-AutomationVariable -Name "<yourunbooktenantidvariable>"
$ClientId = Get-AutomationVariable -Name "<yourunbookclientidvariable>"
$CredentialObject = Get-AutomationPSCredential -Name "<yourrunbookclientsecretsecret>"
$ClientSecret = $CredentialObject.GetNetworkcredential().password
$WebhookURI = Get-AutomationVariable -Name "<yourteamswebhookuri>"
$FunctionURL = Get-AutomationVariable -Name "<yourfunctionapiurl>"
$Body = @{
"tenant" = $TenantId
"client_id" = $ClientId
"scope" = "https://graph.microsoft.com/.default"
"client_secret" = $ClientSecret
"grant_type" = "client_credentials"
}
$Params = @{
"Uri" = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token"
"Method" = "Post"
"Body" = $Body
"ContentType" = "application/x-www-form-urlencoded"
}
$AuthResponse = Invoke-RestMethod @Params
$Header = @{
"Authorization" = "Bearer $($AuthResponse.access_token)"
#"ConsistencyLevel" = "eventual"
}
Function Send-Logs(){
param (
[String]$LogType,
[Hashtable]$LogBodyList
)
$LogBodyJSON = @"
{
"logtype": "$LogType",
"logbody": {}
}
"@
$LogBodyObject = ConvertFrom-JSON $LogBodyJSON
Foreach($Log in $LogBodyList.GetEnumerator()){
$LogBodyObject.logbody | Add-Member -MemberType NoteProperty -Name $log.key -Value $log.value
}
$Body = ConvertTo-JSON $LogBodyObject
$Response = Invoke-Restmethod -uri $FunctionURL -Body $Body -Method POST -ContentType "application/json"
return $Response
}
function Send-ToTeams {
$CurrentTime = Get-Date
$Body = @{
"@context" = "https://schema.org/extensions"
"@type" = "MessageCard"
"themeColor" = "880808"
"title" = ""
"text" = @"
Parameter:
<ul><li>Successful assignments in Intune: $($SuccessChanged.Count)</li><li>Already correct assignment in Intune: $($SuccessAlready.Count)</li><li>Scope tag not found in Intune: $($NoScopeTag.Count)</li><li>Shortname by domain not found in storage table: $($NoShortName.Count)</li><li>Owner not found in Intune: $($NoOwner.Count)</li></ul>
"@
}
$JsonBody = $Body | ConvertTo-JSON
Invoke-RestMethod -Method Post -Body $JsonBody -Uri $WebhookURI | out-null
}
#get all Scope Tags
$uri = "https://graph.microsoft.com/beta/deviceManagement/roleScopeTags"
$Results = Invoke-RestMethod -Method GET -Uri $uri -ContentType "application/json" -Headers $header
$scopeTags = $results.value
if($device){
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$($device)" -ContentType "Application/Json" -Header $Header
$managedDevices += $Results
# $managedDevices
}elseif($Domain){
Add-Type -AssemblyName System.Web
$encodedURL = [System.Web.HttpUtility]::UrlEncode($Domain)
#get android and ios managed devices
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=(contains(activationlockbypasscode,%20%27$encodedURL%27))%20and%20startswith(operatingSystem,'ios')" -ContentType "Application/Json" -Header $Header
$ResultsValue = $Results.value
if ($results."@odata.nextLink" -ne $null) {
$NextPageUri = $results."@odata.nextLink"
##While there is a next page, query it and loop, append results
While ($NextPageUri -ne $null) {
$NextPageRequest = (Invoke-RestMethod -Headers $Header -Uri $NextPageURI -Method Get)
$NxtPageData = $NextPageRequest.Value
$NextPageUri = $NextPageRequest."@odata.nextLink"
$ResultsValue = $ResultsValue + $NxtPageData
}
}
$managedDevices += $ResultsValue
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=(contains(activationlockbypasscode,%20%27$encodedURL%27))%20and%20startswith(operatingSystem,'android')" -ContentType "Application/Json" -Header $Header
$ResultsValue = $Results.value
if ($results."@odata.nextLink" -ne $null) {
$NextPageUri = $results."@odata.nextLink"
##While there is a next page, query it and loop, append results
While ($NextPageUri -ne $null) {
$NextPageRequest = (Invoke-RestMethod -Headers $Header -Uri $NextPageURI -Method Get)
$NxtPageData = $NextPageRequest.Value
$NextPageUri = $NextPageRequest."@odata.nextLink"
$ResultsValue = $ResultsValue + $NxtPageData
}
}
$managedDevices += $ResultsValue
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$($device)" -ContentType "Application/Json" -Header $Header
$managedDevices += $Results
# $managedDevices
}else{
#get android and ios managed devices
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=startswith(operatingSystem,'ios')" -ContentType "Application/Json" -Header $Header
$ResultsValue = $Results.value
if ($results."@odata.nextLink" -ne $null) {
$NextPageUri = $results."@odata.nextLink"
##While there is a next page, query it and loop, append results
While ($NextPageUri -ne $null) {
$NextPageRequest = (Invoke-RestMethod -Headers $Header -Uri $NextPageURI -Method Get)
$NxtPageData = $NextPageRequest.Value
$NextPageUri = $NextPageRequest."@odata.nextLink"
$ResultsValue = $ResultsValue + $NxtPageData
}
}
$managedDevices += $ResultsValue
$Results = Invoke-RestMethod -Method GET -Uri "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=startswith(operatingSystem,'android')" -ContentType "Application/Json" -Header $Header
$ResultsValue = $Results.value
if ($results."@odata.nextLink" -ne $null) {
$NextPageUri = $results."@odata.nextLink"
##While there is a next page, query it and loop, append results
While ($NextPageUri -ne $null) {
$NextPageRequest = (Invoke-RestMethod -Headers $Header -Uri $NextPageURI -Method Get)
$NxtPageData = $NextPageRequest.Value
$NextPageUri = $NextPageRequest."@odata.nextLink"
$ResultsValue = $ResultsValue + $NxtPageData
}
}
$managedDevices += $ResultsValue
}
write-output "Intune Devices: $($managedDevices.count)"
#compare and collect MEID devices, which are managed by intune
foreach($managedDevice in $managedDevices){
#configure hostname if device has an owner
if($managedDevice.userPrincipalName){
#get domainname of user
$dn = ($managedDevice.userprincipalname -split "@")[1]
$shortname = ($hostnames | where {$_.dn -eq $dn}).shortname
if($shortname){
#check if scope tag exists based on azure table
$scopeTag = $scopeTags | where {$_.displayName -like "SCT-INT-$shortname-INTUNE-*-PROD"}
if($scopeTag){
$uri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$($managedDevice.id)')"
$DeviceObject = Invoke-RestMethod -Uri $uri -Headers $header -Method GET -ContentType "application/json"
if($DeviceObject.roleScopeTagIds -contains $scopeTag.id){
$SuccessAlready += $managedDevice.ID
}
else{
$assignBody = @{
roleScopeTagIds = @("$($scopeTag.id)")
}
$JSON = $assignBody | ConvertTo-Json
Invoke-RestMethod -Uri $uri -Headers $header -Method Patch -Body $JSON -ContentType "application/json"
write-output "$($managedDevice.id) - Assigning Scope Tag to $($scopeTag.displayName)"
$SuccessChanged += $managedDevice.ID
}
} else {
write-warning "$($managedDevice.id) - Scope Tag $($scopeTag.displayName) does not exist!"
$NoScopeTag += $managedDevice.ID
}
} else {
write-warning "$($managedDevice.id) - Domain $dn not in Storage Table!"
$NoShortName += $managedDevice.ID
}
} else {
write-warning "$($managedDevice.id) - Device has no Owners"
$NoOwner += $managedDevice.ID
}
}
$Logs = @{
"successchangedassignment"="$($SuccessChanged.count)"
"successalreadyassigned"="$($SuccessAlready.count)"
"errornoscopetag"="$($NoScopeTag.count)"
"errornoshortname"="$($NoShortName.count)"
"errornoowner"="$($NoOwner.count)"
}
Send-Logs -LogType "MobileDeviceScopingByPrimaryUserExecutions" -LogBodyList $Logs
Send-ToTeams