Extensions attributes in AAD are a great way to store any auxiliary information that does not belong in any other user properties. The only sad part, it’s only 15 of them and they are text fields. If you ever need to clear some or all of them, the script below will be a great help!
Benefits of the script:
- makes changes in batches – only takes seconds to run;
- dry or wet run depending on the switch (see the important note below).
This script uses an App-only access rather than Delegated access unlike my other script ( https://365basics.com/powershell-find-and-update-a-string-value-within-all-dynamic-group-membership-rules/ ). If you would like to learn more – https://learn.microsoft.com/en-us/graph/auth/auth-concepts/
Important! This line (#116) of code is responsible for either dry or wet run of the script. I left it commented on purpose and by default.
# $UserAdjustments = Invoke-RestMethod @Parameters
Technically, you can adjust this script to clear and/or populate any user attributes.
# Variables (adjust to yours) $TenantId = 'ab0595b5-8242-202a-8ad2-7c8c98346a1d' $ClientId = '3f35cc15-0eb3-421c-b214-445b8f438d0e' $ClientSecret = 'F_Q8Q~cdfyuGJJD234f2YqCdVAkh8q0l6i~Q2tyR' # Start timer $elapsed = [System.Diagnostics.Stopwatch]::StartNew() # Body data needed for the token request $Body = @{ 'tenant' = $TenantId 'client_id' = $ClientId 'scope' = 'https://graph.microsoft.com/.default' 'client_secret' = $ClientSecret 'grant_type' = 'client_credentials' } # Parameters for the token request $Params = @{ 'Uri' = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" 'Method' = 'Post' 'Body' = $Body 'ContentType' = 'application/x-www-form-urlencoded' } # Get token $AuthResponse = Invoke-RestMethod @Params # Get users and add to collection $Headers = @{ 'Authorization' = "Bearer $($AuthResponse.access_token)" } $Result = Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users?$select=userType,userPrincipalName' -Headers $Headers $AllUsers = @() $AllUsers+=$Result.value # Get more users and add to collection while($Result.'@odata.nextLink' -ne $null) { $Result = Invoke-RestMethod -Method 'Get' -Uri $Result.'@odata.nextLink' -Headers $Headers $AllUsers+=$Result.value } # Stop timer Write-Host "Time to create collection: $($elapsed.Elapsed.ToString())" # Start timer $elapsed = [System.Diagnostics.Stopwatch]::StartNew() # Group users into set batches $Users = $AllUsers | Where-Object {$_.userType -ne 'Guest'} $Counter = [pscustomobject] @{ Value = 0 } $GroupSize = 20 $GroupsOfUsers = $Users | Group-Object -Property { [math]::Floor($Counter.Value++ / $GroupSize) } # Process each batch of users ForEach ($Batch in $GroupsOfUsers) { # Collection for batch requests $myBatchRequests = @() Write-host `n $Groups = $Batch.Group ForEach ($Group in $Groups) { Write-host "Processing:" -NoNewline -ForegroundColor Green $Group.userPrincipalName # Calculate url for each group of users $url = "/users/"+$Group.userPrincipalName+"/onPremisesExtensionAttributes/" # Body $myRequest = @{ id = $Groups.indexof($Group) method = "PATCH" url = $url body = @{ "extensionAttribute1" = "" "extensionAttribute2" = "" "extensionAttribute3" = "" "extensionAttribute4" = "" "extensionAttribute5" = "" "extensionAttribute6" = "" "extensionAttribute7" = "" "extensionAttribute8" = "" "extensionAttribute9" = "" "extensionAttribute10" = "" "extensionAttribute11" = "" "extensionAttribute12" = "" "extensionAttribute13" = "" "extensionAttribute14" = "" "extensionAttribute15" = "" } headers = @{ "Content-Type" = "application/json" } } $myBatchRequests += $myRequest } # Combine batch requests for Graph API batching $allBatchRequests = @{ requests = $myBatchRequests } # Convert body to JSON for Graph API $batchBody = $allBatchRequests | ConvertTo-Json -Depth 5 $batchBody # Parameters for Graph API POST request $Parameters = @{ ContentType = 'application/json' Method = 'POST' Body = $batchBody Uri = 'https://graph.microsoft.com/v1.0/$batch' Headers = @{ Authorization = "Bearer " + $($AuthResponse.access_token) } } # Execute Graph API POST request # $UserAdjustments = Invoke-RestMethod @Parameters Write-host $UserAdjustments.responses.body.error.message -ForegroundColor Red $myBatchRequests = $null } # Stop timer Write-Host "Time spent on requests: $($elapsed.Elapsed.ToString())"
RESULT
Now the question is, how do we null out attrubutes like Mail, for a cloud only user that isnt licensed.
I keep getting “invalid value”.
Hi Daniel,
Have you adjusted that line? – because extension attributes sit underneath onPremisesExtensionAttributes property while Mail does not.
$url = “/users/”+$Group.userPrincipalName+”/onPremisesExtensionAttributes/”