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 ๐