The Azure AD Powershell cmdlets are a bit quirky. They don’t behave like regular PowerShell cmdlets don’t (they don’t seem to work with switches like -Verbose
or -ErrorAction
for instance) and in general seem to be wrappers for some API requests that run behind the scenes. I am thankful they exist of course, coz the last thing I want to do is make API queries, but it would be nice if they behaved better.
Here’s one quirk I ran into today. I wanted to remove licenses from a bunch of users. There’s a cmdlet Set-AzureADUserLicense
and it’s well documented too with an example:
1 2 3 4 5 6 7 |
$LicensedUser = Get-AzureADUser -ObjectId "TemplateUser@contoso.com" $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense $License.SkuId = $LicensedUser.AssignedLicenses.SkuId $Licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses $Licenses.AddLicenses = $License $User = Get-AzureADUser -ObjectId "User@contoso.com" Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $Licenses |
Basically you find a user who already has the licenses you are interested in assigned to them and set these to a variable (that’s lines 1-3 above). This variable is called $License
above and is of type Microsoft.Open.AzureAD.Model.AssignedLicense
. Then you define a new variable (called $Licenses
) of type Microsoft.Open.AzureAD.Model.AssignedLicenses
and assign the licenses captured in the $License
variable to this via an AddLicenses()
method (lines 5 &6).
Finally you assign this to user. Straightforward.
I wanted to remove licenses so I figured why not do the same but in reverse.
The $Licenses.SkuID
property seems to be a string. It looks to be something like this for an example user:
1 |
84a661c4-e949-4bd2-a560-ed7766fcaf2b 2bc9d149-a1dc-4d8f-bcd8-e9c5750a59b5 05e9a617-0261-4cee-bb44-138d3ef5d965 |
That’s three GUIDs corresponding to three licenses this user has. Say I want to remove the first of these licenses, so all I really need to do is the following:
1 2 3 4 5 6 7 8 9 |
$LicensedUser = Get-AzureADUser -ObjectId "TemplateUser@contoso.com" $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense $License.SkuId = $LicensedUser.AssignedLicenses.SkuId # view the GUIDs $License.SkuId # remove the ones I am not interested and assign the rest back to the variable $License.SkuId = "2bc9d149-a1dc-4d8f-bcd8-e9c5750a59b5 05e9a617-0261-4cee-bb44-138d3ef5d965" |
Now let’s create the $Licenses
variable as before and set the above to be removed:
1 2 |
$Licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses $Licenses.RemoveLicenses = $License |
So far so good. Now let me go ahead and set this for a user:
1 2 |
$User = Get-AzureADUser -ObjectId "User@contoso.com" Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $Licenses |
But that doesn’t work! I get the following error:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Set-AzureADUserLicense : Error occurred while executing SetUserLicenses Code: Request_BadRequest Message: Cannot convert a primitive value to the expected type 'Edm.Guid'. See the inner exception for more details. RequestId: 5ec7936d-aea0-43c3-ad67-348402a45415 DateTimeStamp: Mon, 21 Dec 2020 13:24:49 GMT HttpStatusCode: BadRequest HttpStatusDescription: Bad Request HttpResponseStatus: Completed At line:1 char:84 + ... ObjectId)"; Set-AzureADUserLicense -ObjectId $_.ObjectId -AssignedLic ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Set-AzureADUserLicense], ApiException + FullyQualifiedErrorId : Microsoft.Open.AzureAD16.Client.ApiException,Microsoft.Open.AzureAD16.PowerShell.SetUserLicenses |
First I thought maybe it was something to do with my manually removing a license from the variable. Maybe it looks like a string but not really a string and I messed something up. So I tried removing all licenses without any changes and still got the same error.
Then I realized the error message says: Cannot convert a primitive value to the expected type ‘Edm.Guid‘.
Hmm, sounds like wants a GUID instead of the object I was passing. What if I change things a bit to pass the GUIDs? Actually, a single GUID… coz it expects that and not an array of them. Here we go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$LicensedUser = Get-AzureADUser -ObjectId "TemplateUser@contoso.com" $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense $License.SkuId = $LicensedUser.AssignedLicenses.SkuId # view the GUIDs $License.SkuId # remove the ones I am not interested and assign the rest back to the variable # I could assign this to any new variable too, doesn't matter $License.SkuId = "2bc9d149-a1dc-4d8f-bcd8-e9c5750a59b5 05e9a617-0261-4cee-bb44-138d3ef5d965" # now go through each license and remove it foreach ($lic in $($License.SkuId -split ' ')) { Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $lic } |
And that worked! So it turns out removing licenses works differently to adding licenses. When adding you need to pass an object, while for removing you pass the GUID. Also, I don’t really need to get the license object from an existing user… all I need is the GUIDs of the licenses I want to add or remove.
So here’s my adding and removing licenses just to show the difference:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# create an object as before of the single license type $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense # if I want to add a license assign it to the SkuId property. this can take multiple GUIDs. $License.SkuId = "84a661c4-e949-4bd2-a560-ed7766fcaf2b 2bc9d149-a1dc-4d8f-bcd8-e9c5750a59b5" # create an object as before of the multiple license type $Licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses # add the previously created license SKU as something to be added $Licenses.AddLicenses = $License # and to remove any licenses, I just assign them directly. this only takes a single GUID. $Licenses.RemoveLicenes = "05e9a617-0261-4cee-bb44-138d3ef5d965" # just to spice things up here I am then doing the add/ remove across multiple users Get-AzureADUser -All $true | %{ Set-AzureADUserLicense -ObjectId $_.ObjectId -AssignedLicenses $Licenses } |
Update: After writing this post I looked at the properties of the Microsoft.Open.AzureAD.Model.AssignedLicenses
object and to be fair it does show the two methods as being different. My bad for not examining this to begin with.
1 2 3 4 5 6 7 8 9 10 11 12 |
TypeName: Microsoft.Open.AzureAD.Model.AssignedLicenses Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj), bool Equals(Microsoft.Open.AzureAD.Model.AssignedLicenses other), bool IEquatable[AssignedLicenses].Equals(Microsoft.Open.AzureAD.Model.AssignedLicenses other) GetHashCode Method int GetHashCode() GetType Method type GetType() ToJson Method string ToJson() ToString Method string ToString() Validate Method System.Collections.Generic.IEnumerable[System.ComponentModel.DataAnnotations.ValidationResult] Validate(System.ComponentModel.DataAnnotations.ValidationContext validationContext), System.Collect... AddLicenses Property System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.AssignedLicense] AddLicenses {get;set;} RemoveLicenses Property System.Collections.Generic.List[string] RemoveLicenses {get;set;} |
And it looks like RemoveLicenses
too takes an array. So I can pass it multiple GUIDs.