Public/AzureAD/Import-AzureADAppAndPermissions.ps1

function Import-AzureADAppAndPermissions {
    <#
    .SYNOPSIS
    Import Azure AD App name & API permissions from filesystem or GIST-based xml
 
    .DESCRIPTION
    Import Azure AD App name & API permissions from filesystem or GIST-based xml
 
    .PARAMETER Owner
    The owner of the application. For convenience, should be the owner
    that can grant admin consent of the requested API permissions
 
    .PARAMETER XMLPath
    Filesystem path to the XML created by Export-AzureADAppAndPermissions
    Choose this or the Github paramters to grab the xml from a GIST
 
    .PARAMETER GithubUsername
    Github username where the GIST you wish to import lives
 
    .PARAMETER GistFilename
    filename of GIST, example: Test.xml
    This is the most recently created file named, Test.xml, for example.
    If there is more than one filename bypassed
 
    .PARAMETER SecretDuration
    How many years the secret should live.
    Current Options are 1, 2, and None
    1 year, 2 years or None (no secret created)
 
    .PARAMETER Name
    Name of the App to create in the target AzureAD tenant.
    If left blank, will use source tenant app name (plus timestamp of export)
 
    .PARAMETER OpenConsentInBrowser
    Will open the admin consent page where the admin can login to the
    target Azure AD tenant and "grant admin consent" for those APIs that require it
 
    .EXAMPLE
    Import-AzureADAppAndPermissions -Owner admin@thesourceonline.onmicrosoft.com -GithubUsername kevinblumenfeld `
                                    -GistFilename test.xml -Name NewApp09 -SecretDuration 1 -OpenConsentInBrowser
 
    .EXAMPLE
    Import-AzureADAppAndPermissions -Owner admin@thesourceonline.onmicrosoft.com `
                                    -XMLPath C:\Users\kevin\Desktop\Posh365\GraphApps\kevdev\TestApp-20200712-0757.xml`
                                    -Name NewApp01 -SecretDuration 1 -OpenConsentInBrowser
 
    .NOTES
    If SecretDuration is choosen the Secret will be include with the one object this function produces
 
    Example:
 
    ApplicationId : adecd1ee-abcd-4c66-bcf1-4bfb0b610818
    TenantId : f8f6d77c-abcd-4a11-ae04-39bfaff70e00
    ObjectId : c57f335d-abcd-492e-bc90-6cd1a85eecd6
    Secret : pA00W2xLLabcdZLL5g8dS85HiXAjuI1UFKnwdsAlpAk=
 
 
    #>


    [cmdletbinding(DefaultParameterSetName = 'PlaceHolder')]
    param (

        [Parameter(Mandatory, ParameterSetName = 'FileSystem')]
        [Parameter(Mandatory, ParameterSetName = 'GIST')]
        [mailaddress]
        $Owner,

        [Parameter(Mandatory, ParameterSetName = 'FileSystem')]
        [string]
        [ValidateScript( { Test-Path $_ })]
        $XMLPath,

        [Parameter(Mandatory, ParameterSetName = 'GIST')]
        [string]
        $GithubUsername,

        [Parameter(Mandatory, ParameterSetName = 'GIST')]
        [string]
        $GistFilename,

        [Parameter(ParameterSetName = 'FileSystem')]
        [Parameter(ParameterSetName = 'GIST')]
        [ValidateSet('None', 1, 2)]
        $SecretDuration,

        [Parameter(Mandatory, ParameterSetName = 'FileSystem')]
        [Parameter(Mandatory, ParameterSetName = 'GIST')]
        [string]
        $Name,

        [Parameter(ParameterSetName = 'FileSystem')]
        [Parameter(ParameterSetName = 'GIST')]
        [Parameter(ParameterSetName = 'ConnectPoshGraph')]
        [switch]
        $ExportToConnectPoshGraphDelegated,

        [Parameter(ParameterSetName = 'FileSystem')]
        [Parameter(ParameterSetName = 'GIST')]
        [Parameter(ParameterSetName = 'ConnectPoshGraph')]
        [switch]
        $ExportToConnectPoshGraphApplication,

        [Parameter(ParameterSetName = 'FileSystem')]
        [Parameter(ParameterSetName = 'GIST')]
        [switch]
        $OpenConsentInBrowser
    )

    try {
        $AppOwner = Get-AzureADUser -ObjectId $Owner -ErrorAction Stop
        Write-Host "Owner $Owner, found" -ForegroundColor Green
    }
    catch {
        Write-Host "Owner $Owner, not found. Halting script" -ForegroundColor Red
    }
    try {
        $null = Get-AzureADApplication -filter "DisplayName eq '$Name'" -ErrorAction Stop
    }
    catch {
        Write-Host "Azure AD Application Name: $Name already exists" -ForegroundColor Red
        Write-Host "Choose a new name with the -Name parameter" -ForegroundColor Cyan
    }

    if ($PSCmdlet.ParameterSetName -eq 'FileSystem') { $App = Import-Clixml $XMLPath }
    else {
        try {
            $Tempfilepath = Join-Path -Path $Env:TEMP -ChildPath ('{0}.xml' -f [guid]::newguid().guid)
            (Get-GitHubGist -Username $GithubUserName -Filename $GistFilename)[0].content | Set-Content -Path $Tempfilepath -ErrorAction Stop
            $App = Import-Clixml $Tempfilepath
        }
        catch {
            Write-Host "Error importing GIST $($_.Exception.Message)" -ForegroundColor Red
            continue
        }
        finally {
            Remove-Item -Path $Tempfilepath -Force -Confirm:$false -ErrorAction SilentlyContinue
        }

    }
    $Tenant = Get-AzureADTenantDetail
    try {
        $NewAppSplat = @{
            DisplayName = $Name
            ReplyUrls   = $App['SourceApp'].ReplyUrls
            ErrorAction = 'Stop'
        }
        $TargetApp = New-AzureADApplication @NewAppSplat
    }
    catch {
        Write-Host "Unable to create new application: $($_.Exception.Message)" -ForegroundColor Red
    }

    $Output = [ordered]@{ }
    $Output['DisplayName'] = $Name
    $Output['ApplicationId'] = $TargetApp.AppId
    $Output['TenantId'] = $Tenant.ObjectID
    $Output['ObjectId'] = $TargetApp.ObjectId
    $Output['Owner'] = $Owner

    $RequiredList = [System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.RequiredResourceAccess]]::new()
    foreach ($ResourceAppId in $App['API'].keys) {
        $RequiredObject = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]::new()
        $AccessObject = [System.Collections.Generic.List[Microsoft.Open.AzureAD.Model.ResourceAccess]]::new()
        foreach ($ResourceAccess in $App['API'][$ResourceAppId]['ResourceList']) {
            $AccessObject.Add([Microsoft.Open.AzureAD.Model.ResourceAccess]@{
                    Id   = $ResourceAccess.Id
                    Type = $ResourceAccess.Type
                })
        }
        $RequiredObject.ResourceAppId = $ResourceAppId
        $RequiredObject.ResourceAccess = $AccessObject
        $RequiredList.Add($RequiredObject)
    }
    Set-AzureADApplication -ObjectId $TargetApp.ObjectId -RequiredResourceAccess $RequiredList
    $SetSplat = @{ }
    Foreach ($AppOptions in $App['SourceApp'].psobject.properties.name) {
        if ($App['SourceApp'].$AppOptions) {
            $SetSplat[$AppOptions] = $App['SourceApp'].$AppOptions
        }
    }

    Add-AzureADApplicationOwner -ObjectId $TargetApp.ObjectId -RefObjectId $AppOwner.ObjectId

    if ($SecretDuration -ne 'None') {
        $Date = Get-Date
        $Params = @{
            ObjectId            = $TargetApp.ObjectId
            EndDate             = $Date.AddYears($SecretDuration)
            CustomKeyIdentifier = "{0}-{1}" -f $TargetApp.Displayname, $Date.ToString("yyyyMMddTHHmm")
        }
        $SecretResult = New-AzureADApplicationPasswordCredential @Params
        $Output['Secret'] = $SecretResult.value
    }

    Write-Host "Grant Admin Consent by logging in as $Owner here:`r`n" -ForegroundColor Cyan
    $ConsentURL = 'https://login.microsoftonline.com/{0}/v2.0/adminconsent?client_id={1}&state=12345&redirect_uri={2}&scope={3}&prompt=admin_consent' -f @(
        $Tenant.ObjectID, $TargetApp.AppId, 'https://portal.azure.com/', 'https://graph.microsoft.com/.default')

    Write-Host "$ConsentURL" -ForegroundColor White
    [PSCustomObject]$Output
    if ($ExportToConnectPoshGraphDelegated) {
        $SaveSplat = @{
            Tenant        = $Name
            Secret        = $Output['Secret']
            ApplicationId = $Output['ApplicationId']
            TenantId      = $Output['TenantId']
        }
        if ($ExportToConnectPoshGraphDelegated) { $SaveSplat['PromptForDelegatedCredentials'] = $true }
        Save-GraphConfig @SaveSplat
        Write-Host "To connect with Graph use: " -ForegroundColor Cyan
        Write-Host " Connect-PoshGraph -Tenant $Name" -ForegroundColor Green
    }
    if ($OpenConsentInBrowser) { Start $ConsentURL }
}