Script to add Azure AD gallery apps

The Microsoft Graph endpoint for instantiating Azure AD gallery apps is quite new, and I just needed them in order to instantiate 20+ apps based on the GitHub Enterprise Organization template. Here is my code, if someone has use for it ๐Ÿ™‚

I have left out “how to get an access token”. The permissions required is Application.ReadWrite.All.

$accesstoken = "eyJ0eXAiOiJKV1QiLCJub25jZS--- snip ---Cz57c44q23How7d1fVbCJg"


function Ensure-Application
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true,
            Position=0)]
        [String] $AccessToken,

        [Parameter(Mandatory=$false,
            Position=1)]
        [String] $TemplateName = 'GitHub Enterprise Cloud - Organization',

        [Parameter(Mandatory=$false,
            Position=2)]
        [String] $Prefix = 'Github - ',

        [Parameter(Mandatory=$false,
            Position=3)]
        [String] $Suffix = ' (Enterprise Cloud)',

        [Parameter(Mandatory=$false,
            Position=4)]
        [String[]] $Owners = @(),

        [Parameter(Mandatory=$false,
            Position=5)]
        [Boolean] $AppRoleAssignmentRequired = $true,

        [Parameter(Mandatory=$true,
            ValueFromPipeline=$true)]
        [String] $InstanceName

        
    )

    Begin
    {
        # Build rest parameters (providing authorzation header)
        $restParams = @{Headers = @{Authorization = "Bearer $accesstoken"}}

        # Get all templates (returns all without paging)
        $template = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applicationTemplates" @restParams |
            Select-Object -ExpandProperty value | 
            Where-Object displayName -eq $TemplateName

        if(!$template.id) {
            throw "Unable to find template '$TemplateName' in the Azure AD App Gallery"
        } else {
            Write-Debug "Found template: $($template | ConvertTo-Json)"
        }
    }
    Process
    {
        # Calculate displayname
        $DisplayName = "{0}{1}{2}" -f $Prefix, $InstanceName, $Suffix
        Write-Verbose "Calculated displayname '$DisplayName'"

        # Find existing application
        $application = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applications?`$filter=displayName eq '$DisplayName'" @restParams |
            Select-Object -ExpandProperty Value

        # Find existing service principal
        $servicePrincipal = $null
        if($application) {
            $servicePrincipal = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appid eq '$($application.appId)'" @restParams |
                Select-Object -ExpandProperty Value
        }

        if(!$servicePrincipal) {
            Write-Verbose "Instantiating application '$DisplayName'"

            # Instantiate template
            $body = @{
                displayName = $DisplayName
            } | ConvertTo-Json
            $instantiation = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applicationTemplates/$($template.id)/instantiate" -Method Post @restParams -Body $body -ContentType "application/json"

            # Check status of instantiation
            if($instantiation.application.appId) {
                Write-Verbose "Created application '$DisplayName' with id $($instantiation.application.id) and appid $($instantiation.application.appid)"
            } else {
                throw "Something failed when instantiating app :("
            }

            # Get application
            #$application = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applications/$($instantiation.application.id)" @restParams |
            #    Select-Object -ExpandProperty Value

            # Get service principal
            $servicePrincipal = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?`$filter=appid eq '$($instantiation.application.appid)'" @restParams |
                Select-Object -ExpandProperty Value
            
            if(!$servicePrincipal) {
                throw "Could not find service principal for appid $($instantiation.application.appid)"
            }
        } else {
            Write-Verbose "Application '$DisplayName' already exists, checking settings"
        }

        # Ensure appRoleAssignmentRequired is correct
        if($servicePrincipal.appRoleAssignmentRequired -ne $AppRoleAssignmentRequired) {
            Write-Verbose "Updating AppRoleAssignmentRequired from $($servicePrincipal.appRoleAssignmentRequired) to $($AppRoleAssignmentRequired)"
            $body = @{
                appRoleAssignmentRequired = $AppRoleAssignmentRequired
            } | ConvertTo-Json
            Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($servicePrincipal.id)" -Method Patch -Body $body -ContentType "application/json" @restParams 
        }

        # If owners defined, change to these owners
        if($Owners) {
            # Get user objects of owners group Azure AD
            $OwnerObjects = $Owners | ForEach-Object {
                $upn = $_
                try {
                    Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users/$($upn)" @restParams 
                } catch {
                    throw "User $upn not found"
                }
            }

            # Get current owners
            $OwnersInAzureAD = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($servicePrincipal.id)/owners" @restParams | 
                Select-Object -ExpandProperty Value

            # For each owner sendt in parameter, ensure they are added as owner
            $OwnerObjects | 
                Where-Object {
                    $_.id -notin $OwnersInAzureAD.id
                } |
                ForEach-Object {
                    Write-Verbose "Adding user $($_.userPrincipalName) as owner of app"
                    $body = @{
                        "@odata.id" = "https://graph.microsoft.com/v1.0/users/{0}" -f $_.id
                    } | ConvertTo-Json
                    Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($servicePrincipal.id)/owners/`$ref" @restParams -Body $body -ContentType "application/json" -Method Post
                }

            # For each owner ni Azure AD, that should not be there, ensure they are removed as owner
            $OwnersInAzureAD  | 
                Where-Object {
                    $_.id -notin $OwnerObjects.id
                } |
                ForEach-Object {
                    Write-Verbose "Removing user $($_.id) as owner of app"
                    Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($servicePrincipal.id)/owners/$($_.id)/`$ref" @restParams -Method Delete
                }
        }
    }
    End
    {

    }
}

Ensure-Application -AccessToken $accesstoken -InstanceName "Test1" -Verbose -Owners @("AdeleV@M365x330461.OnMicrosoft.com")
Ensure-Application -AccessToken $accesstoken -InstanceName "Test2" -Verbose -Owners @("AdeleV@M365x330461.OnMicrosoft.com")
Ensure-Application -AccessToken $accesstoken -InstanceName "Test3" -Verbose -Owners @("AdeleV@M365x330461.OnMicrosoft.com")

This will create three applications in your tenant, all based on the same Azure AD gallery app, and add owners to the app. The script can be run several times and will only create the app with the same name once.

Good luck ๐Ÿ™‚

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s