DimeScheduler.InstallExchangeApp.ps1

<#PSScriptInfo
.VERSION 0.0.1.0
.GUID 4812b98b-f5e4-48e4-8dfd-05a0d347178b
.AUTHOR Hendrik Bulens
.COMPANYNAME Dime Software
.ICONURI https://cdn.dimescheduler.com/dime-scheduler/v2/shape.png
#>


<#
.SYNOPSIS
Creates a Microsoft Entra ID App Registration for the Dime.Scheduler Exchange connector.
 
.DESCRIPTION
Creates a Microsoft Entra ID App Registration for the Dime.Scheduler Exchange connector.
 
.PARAMETER tenantId
Required.
 
.PARAMETER tenantName
Required.
 
.PARAMETER appName
Optional. The app name: Dime.Scheduler by default.
 
.PARAMETER applicationPermissions
Optional. The permission set to add to the app.
#>


param (    
    [Parameter(mandatory = $true, HelpMessage = "The id of the Azure tenant ")]
    [string] $tenantId,

    [Parameter(mandatory = $true, HelpMessage = "The name of the Azure tenant ")]
    [string] $tenantName,

    [Parameter(mandatory = $false, HelpMessage = "The name of the MS Entra ID App.")]
    [string] $appName = "Dime.Scheduler [Exchange]",

    [Parameter(mandatory = $false, HelpMessage = "The permission set")]
    [string[]] $applicationPermissions = @('Calendars.ReadWrite', 'MailboxSettings.ReadWrite', 'User.Read.All')
)

$global:appId = $null;
$global:clientSecretKey = $null;

Function New-MSEntraIdApp {
    param (
        [string] $displayName,
        [string] $tenantName
    )    
    $app = az ad app list --display-name $displayName | ConvertFrom-Json

    if ($app.length -eq 0) {
        Write-Host "MS Entra ID App '$displayName' does not exist yet. Creating..." -ForegroundColor DarkYellow

        $app = az ad app create `
            --display-name $displayName `
            --web-home-page-url "https://app.dimescheduler.com" `
            --web-redirect-uris "https://app.dimescheduler.com/signin-microsoft" `
            --identifier-uris "https://app.dimescheduler.com/" | ConvertFrom-Json                

        Write-Host "MS Entra ID App '$displayName' has been created!" -ForegroundColor DarkGreen
    }
    else {        
        Write-Host "MS Entra ID App '$displayName' already exists." -ForegroundColor DarkGreen
        $app = $app[0];        
    }
  
    Write-Host "Creating secret..." -ForegroundColor DarkYellow   
    $date = Get-Date -Format "yyyyMMdd"
    $customIdentifier = "Dime.Scheduler-$date"
    $clientSecret = az ad app credential reset --id $app.appId --years 20 --append --query password -o tsv
    $global:clientSecretKey = $clientSecret
        
    $clientSecret = az ad app credential reset --id $app.appId --years 20 --append --query password -o tsv
    $global:clientSecretKey = $clientSecret
    Write-Host "Created secret!" -ForegroundColor DarkGreen    

    # Create a service principal for the app
    # This is necessary to be able to grant the application the required permissions
    $spForApp = az ad sp create --id $app.appId

    $global:appId = $app.appId

    return $app
}

Function Grant-AdminConsent {
    param ($appId)

    Write-Host "Granting admin consent..." -ForegroundColor DarkYellow   
    az ad app permission admin-consent --id $appId
    Write-Host "Granted admin consent!" -ForegroundColor DarkGreen 
}

Function Add-Permissions {
    param (
        [string] $targetServicePrincipalName,
        $appPermissionsRequired,
        $childApp,
        $spForApp
    )

    $graphId = az ad sp list --query "[?appDisplayName=='Microsoft Graph'].appId | [0]" --all

    # Iterate Permissions array
    Write-Host "Retrieving role assignments objects..."

    $roleAssignments = [System.Collections.ArrayList]::new()
    foreach ($appPermission in $appPermissionsRequired) {        

        Write-Host "Retrieving $appPermission " -ForegroundColor DarkYellow
        $roleAssignment = az ad sp show --id $graphId --query "appRoles[?value=='$appPermission'].id | [0]"

        if ($roleAssignment) {
            Write-Host "$roleAssignment" -ForegroundColor DarkGreen
            [void]$roleAssignments.Add(($roleAssignment.Replace("`"", "")))
        }
        else {
            Write-Host "$appPermission was not found!" -ForegroundColor DarkYellow
        }        
    }

    $requiredResourceAccessObjects = [System.Collections.ArrayList]::new()
    foreach ($roleAssignment in $roleAssignments) {        
        [void]$requiredResourceAccessObjects.Add(@{
                id   = $roleAssignment
                type = "Role"
            })
    }
        
    $requiredResourceAccess = @{
        "resourceAppId"  = $graphId.Replace("`"", "")
        "resourceAccess" = $requiredResourceAccessObjects 
    } 
            
    $requiredResourceAccessArray = $requiredResourceAccess | ConvertTo-Json -AsArray -Depth 6 -Compress
    $jsonString = ($requiredResourceAccessArray | Out-String) -replace '"', '\"'

    Write-Host $requiredResourceAccessArray -ForegroundColor DarkYellow

    az ad app update --id $childApp.appId --required-resource-accesses $jsonString

    Write-Host "Addded permissions to app." -ForegroundColor DarkGreen   
}

Write-Output ""
Write-Output ""
Write-Output "*"
Write-Output "**"
Write-Output "***"
Write-Output "****"
Write-Output "*****"
Write-Output "******"
Write-Output "*******"
Write-Output "********"
Write-Output "*********"
Write-Output "**********"
Write-Output "***********"
Write-Output "************"
Write-Output ""
Write-Output "_____ _ _____ _ _ _"
Write-Output "| __ \(_) / ____| | | | | | |"
Write-Output "| | | |_ _ __ ___ ___ | (___ ___| |__ ___ __| |_ _| | ___ _ __"
Write-Output "| | | | | '_ ` _ \ / _ \ \___ \ / __| '_ \ / _ \/ _` | | | | |/ _ \ '__|"
Write-Output "| |__| | | | | | | | __/_ ____) | (__| | | | __/ (_| | |_| | | __/ |"
Write-Output "|_____/|_|_| |_| |_|\___(_)_____/ \___|_| |_|\___|\__,_|\__,_|_|\___|_|"
Write-Output ""
Write-Output ""
Write-Output ""
Write-Output "************"
Write-Output "***********"
Write-Output "**********"
Write-Output "*********"
Write-Output "********"
Write-Output "*******"
Write-Output "******"
Write-Output "*****"
Write-Output "****"
Write-Output "***"
Write-Output "**"
Write-Output "*"
Write-Output ""
Write-Output ""

# Login to Azure
az login --tenant $tenantId

# Create MS Entra ID App
$app = New-MSEntraIdApp -displayName $appName -tenantName $tenantName

# Add application permissions
$spForApp = az ad sp list --filter "appId eq '$($app.appId)'" | ConvertFrom-Json

Add-Permissions -targetServicePrincipalName 'Microsoft Graph' -appPermissionsRequired $applicationPermissions -childApp $app -spForApp $spForApp[0]
Grant-AdminConsent -appId $app.appId

Write-Host ""
Write-Host ""
Write-Host "******************"
Write-Host "Copy the application id to complete the installation of Dime.Scheduler"
Write-Host $global:appId
Write-Host "******************"
Write-Host "Copy the client secret to complete the installation of Dime.Scheduler"
Write-Host "WARNING: once you close this window, you won't be able to recover the client secret."
Write-Host $global:clientSecretKey
Write-Host "******************"