Search code examples
powershellazure-active-directorymicrosoft-graph-apientra

Assigning Roles to a Group and scoping that to an Admin Unit programatically in Entra


I got a few question related to a module I'm unfamiliar with Thank you all in advance for any help.

First off here is my code:

Import-Module Microsoft.Graph.Identity.Governance
Import-Module Microsoft.Graph.Identity.DirectoryManagement
Import-Module Microsoft.Graph.Authentication
connect-mggraph -Scopes "Group.ReadWrite.All,RoleManagement.ReadWrite.Directory" -Tenant "TenantId"

$GroupList = Import-CSV -path "PathHere"

foreach ($Group in $GroupList) {
    #Convert variables for easier handling
    $Name = $Group.Name
    $NickName = $Group.NickName
    $AuName = $Group.AU
    #Make a new role enabled group and grab its object id
    $Id = New-MgGroup -DisplayName $Name -MailEnabled:$False  -MailNickName $NickName -SecurityEnabled -IsAssignableToRole -Visibility "Private" | Select Id
    #Get the object id of an Admin unit I made earlier
    $AdminUnit = Get-MgDirectoryAdministrativeUnit -Filter "DisplayName eq '$AuName'" | Select Id
   
    #This part is stolen from Microsoft documentation
    $params = @{
      "@odata.type" = "#microsoft.graph.unifiedRoleAssignment"
      #Auth Admin
    roleDefinitionId = "c4e39bd9-1100-46d3-8c65-fb160da0071f"
      principalId = $Id
      directoryScopeId = "/administrativeUnits/{0}" -f $AdminUnit
}

New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $params

}

With my above Code I'm getting the following error.

New-MgRoleManagementDirectoryRoleAssignment : Invalid GUID:@{Id=0c504d37-f6f7-4223-a8f5-ec086a6ba272}
    Status: 400 (BadRequest)

I think it's not liking the $Id variable I'm passing to it, however it's clearly providing the correct Object Id so I think it may be a formatting issue. I'm unsure what to do to correct it however so if anyone could provide some insight there I would appreciate it.

Secondly, I do not think

directoryScopeId = "/administrativeUnits/{0}" -f $AdminUnit 

Is going to work so this is a preemptive "is there a better way to feed a variable into a string?"

My third and final question, is there a better way to achieve this overall via PowerShell? My end goal is to assign a built in role to a single Group, and then scope that to a single Admin Unit and then work through an entire list of these. Is there a better module I could use for this or a more efficient way to logic this out?


Solution

  • The issue with your code is that in both cases you're missing -ExpandProperty in the Select-Object calls, because of that instead of getting a value you end up with an object having a single property (Id). Then $Id and $AdminUnit are passed as stringified objects causing the API response error. You can also clearly see it when looking at the API response:

    Invalid GUID:@{Id=0c504d37-f6f7-4223-a8f5-ec086a6ba272}

    The @{...} happens when a custom object is converted to a string.

    As for the final question, a more efficient way to do it, this depends. The API to create groups will only allow to create single group per call, however you could create them in parallel using ForEach-Object -Parallel or by using the $batch API, in both cases the code will get much more complex, for the former you will need to handle TooManyRequests (429) API responses and for the latter you will not be able to use the cmdlets, will need to construct the API calls yourself. One improvement, assuming the AUs are repeated, is to add a caching mechanism so you don't query for them more than one time (added in the code).

    Aside from that the code looks correct, it might be also worth adding a guard clause in case the AU couldn't be found.

    # defines a hashtable (the caching mechanism named before)
    $aus = @{}
    
    foreach ($Group in $GroupList) {
        #Make a new role enabled group and grab its object id
        $newMgGroupSplat = @{
            DisplayName        = $Group.Name
            MailEnabled        = $False
            MailNickName       = $Group.NickName
            SecurityEnabled    = $true
            IsAssignableToRole = $true
            Visibility         = 'Private'
        }
        $mgGroup = New-MgGroup @newMgGroupSplat
    
        # if the AU doesnt exist in the hashtable
        if (-not $aus.ContainsKey($Group.AU)) {
            # query it
            $au = Get-MgDirectoryAdministrativeUnit -Filter "DisplayName eq '$($Group.AU)'"
            # if its not found
            if (-not $au) {
                # go to the next loop iteration
                Write-Warning "'$($Group.AU)' could not be found."
                return
            }
            # else, add it to the hashtable (Key = DisplayName // Value = AU ID)
            $aus[$Group.Id] = $au.Id
        }
    
        #This part is stolen from Microsoft documentation
        $params = @{
            '@odata.type'    = '#microsoft.graph.unifiedRoleAssignment'
            roleDefinitionId = 'c4e39bd9-1100-46d3-8c65-fb160da0071f'
            principalId      = $mgGroup.Id
            directoryScopeId = '/administrativeUnits/{0}' -f $aus[$Group.Id]
        }
        New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $params
    }