Scripts/Set-D365ApplicationUser.ps1

#
# Set-D365ApplicationUser.ps1
#

function Add-D365ApplicationUser {
    Param(
        [Parameter(Mandatory = $true)] [string] $d365ResourceName  
        , [Parameter(Mandatory = $true)] [string] $servicePrincipal
        , [Parameter(Mandatory = $true)] [string] $roleNames
    )

    $securityRoleNames = $roleNames.Split(',')
    $d365ResourceName = $d365ResourceName.Replace(".dynamics.com/", ".dynamics.com")
    # Forcing Tls 1.2
    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

    $aToken = $(az account get-access-token --resource $d365ResourceName) | ConvertFrom-Json

    $accessToken = $aToken.accessToken

    function Get-DefaultBusinessUnit {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $response = Invoke-RestMethod `
            -Uri "$apiUrl/businessunits?`$select=businessunitid,name&`$filter=parentbusinessunitid eq null" `
            -Method Get `
            -Headers @{"Authorization" = "Bearer $accessToken"; "Accept" = "application/json"; "OData-MaxVersion" = "4.0"; "OData-Version" = "4.0" }
  
        return $response.value[0].businessunitid
    }

    function Get-ApplicationUser {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken,
            [string] [Parameter(Mandatory = $true)] $applicationId,
            [string] [Parameter(Mandatory = $true)] $businessUnitId
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $headers = @{
            "Authorization"    = "Bearer $accessToken";
            "Accept"           = "application/json";
            "OData-MaxVersion" = "4.0";
            "OData-Version"    = "4.0"
        }

        $response = Invoke-RestMethod `
            -Uri "$apiUrl/systemusers?`$select=systemuserid,applicationid&`$filter=applicationid eq '$applicationId' and businessunitid/businessunitid eq '$businessUnitId'" `
            -Method Get `
            -Headers $headers

        if ($response.value.count -gt 0) {
            return $response.value[0]
        }
    }

    function New-ApplicationUser {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken,
            [string] [Parameter(Mandatory = $true)] $applicationId,
            [string] [Parameter(Mandatory = $true)] $businessUnitId
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $headers = @{
            "Authorization"    = "Bearer $accessToken";
            "Accept"           = "application/json";
            "OData-MaxVersion" = "4.0";
            "OData-Version"    = "4.0";
            "Prefer"           = "return=representation"
        }    

        $body = @{
            "applicationid"             = "$applicationId";
            "internalemailaddress"      = "$applicationId@$($([System.Uri]$d365ResourceName).Host)";
            "businessunitid@odata.bind" = "/businessunits($businessUnitId)"
        }

        $response = Invoke-RestMethod `
            -Uri "$apiUrl/systemusers" `
            -Method Post `
            -Headers $headers `
            -ContentType "application/json" `
            -Body ($body | ConvertTo-Json)
  
        return $response  
    }

    function Set-ApplicationUser {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken,
            [string] [Parameter(Mandatory = $true)] $systemUserId,
            [string] [Parameter(Mandatory = $true)] $applicationId,
            [string] [Parameter(Mandatory = $true)] $businessUnitId
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $headers = @{
            "Authorization"    = "Bearer $accessToken";
            "Accept"           = "application/json";
            "OData-MaxVersion" = "4.0";
            "OData-Version"    = "4.0";
            "Prefer"           = "return=representation"
        }

        $body = @{
            "applicationid"             = "$applicationId";
            "internalemailaddress"      = "$applicationId@$($([System.Uri]$d365ResourceName).Host)";
            "businessunitid@odata.bind" = "/businessunits($businessUnitId)";
            "isdisabled"                = "$false";
        }

        $response = Invoke-RestMethod `
            -Uri "$apiUrl/systemusers($systemUserId)" `
            -Method PATCH `
            -Headers $headers `
            -ContentType "application/json" `
            -Body ($body | ConvertTo-Json)

        return $response;
    }

    function Get-Role {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken,
            [string] [Parameter(Mandatory = $true)] $roleName,
            [string] [Parameter(Mandatory = $true)] $businessUnitId
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $headers = @{
            "Authorization"    = "Bearer $accessToken";
            "Accept"           = "application/json";
            "OData-MaxVersion" = "4.0";
            "OData-Version"    = "4.0"
        }

        $response = Invoke-RestMethod `
            -Uri "$apiUrl/roles?`$select=roleid&`$filter= name eq '$roleName' and businessunitid/businessunitid eq '$businessUnitId'" `
            -Method Get `
            -Headers $headers

        if ($response.value.count -gt 0) {
            return $response.value[0]
        }
    }

    function Set-ApplicationUserRole {
        param (
            [string] [Parameter(Mandatory = $true)] $d365ResourceName,
            [string] [Parameter(Mandatory = $true)] $accessToken,
            [string] [Parameter(Mandatory = $true)] $systemUserId,
            [string] [Parameter(Mandatory = $true)] $roleId
        )

        $apiUrl = "$d365ResourceName/api/data/v9.1"

        $headers = @{
            "Authorization"    = "Bearer $accessToken";
            "Accept"           = "application/json";
            "OData-MaxVersion" = "4.0";
            "OData-Version"    = "4.0";
        }

        $body = @{
            '@odata.id' = "$apiUrl/roles($roleId)"
        }

        Invoke-RestMethod `
            -Uri "$apiUrl/systemusers($systemUserId)/systemuserroles_association/`$ref" `
            -Method Post `
            -Headers $headers `
            -ContentType "application/json" `
            -Body ($body | ConvertTo-Json) `
        | Out-Null
    }
    

    $businessUnitId = Get-DefaultBusinessUnit `
        -d365ResourceName $d365ResourceName `
        -accessToken $accessToken
    
    $applicationUser = Get-ApplicationUser `
        -d365ResourceName $d365ResourceName `
        -accessToken $accessToken `
        -applicationId $servicePrincipal `
        -businessUnitId $businessUnitId
    
    if (!$applicationUser) {
        Write-Host "`nCreating application user..."
        Write-Host $businessUnitId
        $applicationUser = New-ApplicationUser `
            -d365ResourceName $d365ResourceName `
            -accessToken $accessToken `
            -applicationId $servicePrincipal `
            -businessUnitId $businessUnitId
        Write-Host "Application user created..."
    }
    else {
        Write-Host "`nUpdating application user..."
        $applicationUser = Set-ApplicationUser `
            -d365ResourceName $d365ResourceName `
            -accessToken $accessToken `
            -systemUserId $applicationUser.systemuserid `
            -applicationId $servicePrincipal `
            -businessUnitId $businessUnitId    
        Write-Host "Application user updated..."
    }
    
    foreach ($securityRoleName in $securityRoleNames) {
        Write-Host "`nChecking if the security role $securityRoleName exists..."
        $securityRole = Get-Role `
            -d365ResourceName $d365ResourceName `
            -accessToken $accessToken `
            -roleName $securityRoleName `
            -businessUnitId $businessUnitId
        
        if (!$securityRole) {
            Write-Host "$securityRoleName does not exist."
            return
        }
    
        Write-Host "Assigning role $securityRoleName to application user $($servicePrincipal)..."
        Set-ApplicationUserRole `
            -d365ResourceName $d365ResourceName `
            -accessToken $accessToken `
            -systemUserId $applicationUser.systemuserid `
            -roleId $securityRole.roleid
        Write-Host "Role $securityRoleName assigned to application user $($servicePrincipal)."
    }    

}