source/Public/Set-RJAutomationAccount.ps1

function Set-RJAutomationAccount {
    <#
    .SYNOPSIS
    Deploys RealmJoin Automation Account infrastructure to Azure.
 
    .DESCRIPTION
    Deploys the RealmJoin Automation Account with a system-assigned managed identity and grants it
    the API permissions and the Entra ID directory roles the RealmJoin runbooks need. Both lists come
    from the RealmJoin runbooks repository on GitHub and are downloaded on every run, so they always
    match the current runbooks.
 
    Uses bundled ARM templates (JSON).
 
    Prerequisites:
    - Az.Accounts and Az.Resources PowerShell modules
    - Resource group must already exist
    - The signed-in account holds the Privileged Role Administrator or Global Administrator role
      (required to assign the directory roles)
 
    .PARAMETER ResourceGroupName
    The name of the resource group to deploy to (must exist).
 
    .PARAMETER AutomationAccountName
    Optional. The name of the Automation Account. If not provided, a unique name is auto-generated.
 
    .PARAMETER SubscriptionId
    Optional. The Azure subscription ID to deploy to. If not provided, uses the current context.
 
    .PARAMETER Token
    Optional. The RealmJoin onboarding token. When provided, registers the automation account with RealmJoin API.
 
    .PARAMETER RunbookBranch
    Optional. Branch of the runbooks repository whose published permission set and directory roles
    are assigned. Defaults to 'production'. Use the branch your tenant follows in the RealmJoin
    Portal's runbook settings. Cannot be combined with -PermissionsManifestUrl or -DirectoryRolesManifestUrl.
 
    .PARAMETER PermissionsManifestUrl
    Optional. URL of a permissions manifest in the format of AllRealmJoinRunbooks_collected_permissions.json
    from the runbooks repository, e.g. a fork, another repository or a pinned commit on
    raw.githubusercontent.com. Cannot be combined with -RunbookBranch.
 
    .PARAMETER DirectoryRolesManifestUrl
    Optional. URL of a directory roles manifest in the format of AllRealmJoinRunbooks_collected_rbacroles.json
    from the runbooks repository (a JSON array of role display names). When omitted alongside
    -PermissionsManifestUrl, the roles published on the production branch are used.
    Cannot be combined with -RunbookBranch.
 
    .PARAMETER WhatIf
    Shows what would be deployed without actually performing the deployment.
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin"
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -AutomationAccountName "my-aa"
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -SubscriptionId "00000000-0000-0000-0000-000000000000"
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -WhatIf
    Shows what resources would be deployed without actually deploying.
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -Token "your-token"
    Deploys and registers with the RealmJoin API.
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -RunbookBranch "master"
    Assigns the permission set and directory roles published on the runbooks repository's master branch.
 
    .EXAMPLE
    Set-RJAutomationAccount -ResourceGroupName "rg-realmjoin" -PermissionsManifestUrl "https://raw.githubusercontent.com/realmjoin/realmjoin-runbooks/<commit>/docs/other/json/AllRealmJoinRunbooks_collected_permissions.json" -DirectoryRolesManifestUrl "https://raw.githubusercontent.com/realmjoin/realmjoin-runbooks/<commit>/docs/other/json/AllRealmJoinRunbooks_collected_rbacroles.json"
    Assigns the permission set and directory roles published at a pinned commit.
    #>


    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Published')]
    param(
        [Parameter(Mandatory)]
        [string]$ResourceGroupName,

        [string]$AutomationAccountName,

        [string]$SubscriptionId,

        [string]$Token,

        [Parameter(ParameterSetName = 'Published')]
        [ValidateNotNullOrEmpty()]
        [string]$RunbookBranch = 'production',

        [Parameter(ParameterSetName = 'CustomUrl')]
        [ValidatePattern('^https://')]
        [string]$PermissionsManifestUrl,

        [Parameter(ParameterSetName = 'CustomUrl')]
        [ValidatePattern('^https://')]
        [string]$DirectoryRolesManifestUrl
    )

    begin {
        Write-Verbose "Set-RJAutomationAccount: Starting"
    }

    process {
        if (-not (Test-RJModuleVersion)) {
            # Module is outdated - exit gracefully without throwing
            return
        }

        # Initialize Az modules
        Write-Information "Initializing required Azure modules..."
        if (-not (Initialize-RequiredModule)) {
            Write-Verbose "Azure module initialization failed - cannot proceed"
            return
        }

        # Connect to Azure
        $connectParams = @{}
        if ($SubscriptionId) { $connectParams.SubscriptionId = $SubscriptionId }
        $context = Connect-RJAzureContext @connectParams
        if ($null -eq $context) {
            Write-Information "Operation cancelled."
            return
        }

        # Verify resource group
        $resourceGroup = Test-ResourceGroup -ResourceGroupName $ResourceGroupName

        $operation = if ($Token) { "Deploy RealmJoin Automation Account and register with the RealmJoin API" } else { "Deploy RealmJoin Automation Account" }
        if ($PSCmdlet.ShouldProcess("Resource Group: $ResourceGroupName", $operation)) {
            Write-Information ""
            Write-Information "========================================"
            Write-Information "RealmJoin Automation Account Deployment"
            Write-Information "========================================"
            Write-Information ""

            # Deploy
            $deployParams = @{
                ResourceGroupName = $ResourceGroupName
                RunbookBranch     = $RunbookBranch
            }
            if ($AutomationAccountName) {
                $deployParams.AutomationAccountName = $AutomationAccountName
            }
            if ($PermissionsManifestUrl) {
                $deployParams.PermissionsManifestUrl = $PermissionsManifestUrl
            }
            if ($DirectoryRolesManifestUrl) {
                $deployParams.DirectoryRolesManifestUrl = $DirectoryRolesManifestUrl
            }

            $result = Set-RJAutomationAccountResource @deployParams

            if (-not $result.Success) {
                throw "Automation Account deployment failed"
            }

            # Optional API registration
            if ($Token) {
                Write-Information ""
                Write-Information "--- RealmJoin API Registration ---"
                $additionalData = @{
                    tenantId              = $context.TenantId
                    subscriptionId        = $context.SubscriptionId
                    resourceGroup         = $ResourceGroupName
                    automationAccountName = $result.Outputs.AutomationAccountName
                    location              = $resourceGroup.Location
                }
                try {
                    Invoke-RJOnboarding -Token $Token -ApiUrl (Get-RealmJoinTenantRunbooksConfigApiUrl) -AdditionalData $additionalData
                }
                catch {
                    # The Azure deployment already succeeded. Surfacing this as a terminating error would make
                    # the interactive retry loop redeploy and re-send the (single-use) onboarding token.
                    Write-Warning "RealmJoin API registration failed: $($_.Exception.Message)"
                    Write-Warning "The Automation Account was deployed successfully. Re-run with a new onboarding token to register it."
                }
            }

            # Summary
            Write-Information ""
            Write-Information "========================================"
            Write-Information "Deployment Complete!"
            Write-Information "========================================"
            Write-Information "Automation Account: $($result.Outputs.AutomationAccountName)"
            Write-Information "Resource ID: $($result.Outputs.AutomationAccountId)"
            Write-Information ""
        }
        else {
            # WhatIf: show analysis
            $analysis = Get-RJAzureResourceAnalysis -ResourceGroupName $ResourceGroupName `
                -DeployAutomationAccount $true `
                -DeployLogAnalytics $false `
                -AutomationAccountName $AutomationAccountName `
                -PermissionsManifestUrl $PermissionsManifestUrl `
                -DirectoryRolesManifestUrl $DirectoryRolesManifestUrl `
                -RunbookBranch $RunbookBranch
            Show-RJAzureResourceAnalysis -Analysis $analysis
        }
    }

    end {
        Write-Verbose "Set-RJAutomationAccount: Completed"
    }
}