284 строки
13 KiB
PowerShell
284 строки
13 KiB
PowerShell
# --------------------------------------------------------------------------------------------
|
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
# Licensed under the MIT license.
|
|
# --------------------------------------------------------------------------------------------
|
|
|
|
[CmdletBinding()]
|
|
param (
|
|
[string]$ResourceGroupName = "AssessmentAppLTI",
|
|
[switch]$UseActiveAzureAccount,
|
|
[string]$SubscriptionNameOrId = $null,
|
|
[string]$LocationName = $null
|
|
)
|
|
|
|
process {
|
|
function Write-Title([string]$Title) {
|
|
Write-Host "`n`n============================================================="
|
|
Write-Host $Title
|
|
Write-Host "=============================================================`n`n"
|
|
}
|
|
|
|
try {
|
|
|
|
#region Setup Logging
|
|
. .\Write-Log.ps1
|
|
$ScriptPath = split-path -parent $MyInvocation.MyCommand.Definition
|
|
$ExecutionStartTime = $(get-date -f dd-MM-yyyy-HH-mm-ss)
|
|
$LogRoot = Join-Path $ScriptPath "Log"
|
|
|
|
$LogFile = Join-Path $LogRoot "Log-$ExecutionStartTime.log"
|
|
Set-LogFile -Path $LogFile
|
|
|
|
$TranscriptFile = Join-Path $LogRoot "Transcript-$ExecutionStartTime.log"
|
|
Start-Transcript -Path $TranscriptFile;
|
|
#endregion
|
|
|
|
#region Login to Azure CLI
|
|
Write-Title 'STEP #1 - Logging into Azure'
|
|
|
|
function Test-LtiActiveAzAccount {
|
|
$account = az account show | ConvertFrom-Json
|
|
if(!$account) {
|
|
throw "Error while trying to get Active Account Info."
|
|
}
|
|
}
|
|
|
|
function Connect-LtiAzAccount {
|
|
$loginOp = az login | ConvertFrom-Json
|
|
if(!$loginOp) {
|
|
throw "Encountered an Error while trying to Login."
|
|
}
|
|
}
|
|
|
|
if ($UseActiveAzureAccount) {
|
|
Write-Log -Message "Using Active Azure Account"
|
|
Test-LtiActiveAzAccount
|
|
}
|
|
else {
|
|
Write-Log -Message "Logging in to Azure"
|
|
Connect-LtiAzAccount
|
|
}
|
|
|
|
Write-Log -Message "Successfully logged in to Azure."
|
|
#endregion
|
|
|
|
#region Choose Active Subcription
|
|
Write-Title 'STEP #2 - Choose Subscription'
|
|
|
|
function Get-LtiSubscriptionList {
|
|
$AzAccountList = ((az account list --all --output json) | ConvertFrom-Json)
|
|
if(!$AzAccountList) {
|
|
throw "Encountered an Error while trying to fetch Subscription List."
|
|
}
|
|
Write-Output $AzAccountList
|
|
}
|
|
|
|
function Set-LtiActiveSubscription {
|
|
param (
|
|
[string]$NameOrId,
|
|
$List
|
|
)
|
|
|
|
$subscription = ($List | Where-Object { ($_.name -ieq $NameOrId) -or ($_.id -ieq $NameOrId) })
|
|
if(!$subscription) {
|
|
throw "Invalid Subscription Name/ID Entered."
|
|
}
|
|
az account set --subscription $NameOrId
|
|
#Intentionally not catching an exception here since the set subscription commands behavior (output) is different from others
|
|
|
|
Write-Output $subscription
|
|
}
|
|
|
|
Write-Log -Message "Fetching List of Subscriptions in Users Account"
|
|
$SubscriptionList = Get-LtiSubscriptionList
|
|
Write-Log -Message "List of Subscriptions:-`n$($SubscriptionList | ConvertTo-Json -Compress)"
|
|
|
|
$SubscriptionCount = ($SubscriptionList | Measure-Object).Count
|
|
Write-Log -Message "Count of Subscriptions: $SubscriptionCount"
|
|
if ($SubscriptionCount -eq 0) {
|
|
throw "Please create at least ONE Subscription in your Azure Account"
|
|
}
|
|
elseif ($SubscriptionNameOrId) {
|
|
Write-Log -Message "Using User provided Subscription Name/ID: $SubscriptionNameOrId"
|
|
}
|
|
elseif ($SubscriptionCount -eq 1) {
|
|
$SubscriptionNameOrId = $SubscriptionList[0].id;
|
|
Write-Log -Message "Defaulting to Subscription ID: $SubscriptionNameOrId"
|
|
}
|
|
else {
|
|
$SubscriptionListOutput = $SubscriptionList | Select-Object @{ l="Subscription Name"; e={ $_.name } }, "id", "isDefault"
|
|
Write-Host ($SubscriptionListOutput | Out-String)
|
|
$SubscriptionNameOrId = Read-Host 'Enter the Name or ID of the Subscription from Above List'
|
|
#trimming the input for empty spaces, if any
|
|
$SubscriptionNameOrId = $SubscriptionNameOrId.Trim()
|
|
Write-Log -Message "User Entered Subscription Name/ID: $SubscriptionNameOrId"
|
|
}
|
|
|
|
$ActiveSubscription = Set-LtiActiveSubscription -NameOrId $SubscriptionNameOrId -List $SubscriptionList
|
|
$UserEmailAddress = $ActiveSubscription.user.name
|
|
#endregion
|
|
|
|
#region Choose Region for Deployment
|
|
Write-Title "STEP #3 - Choose Location`n(Please refer to the Documentation / ReadMe on Github for the List of Supported Locations)"
|
|
|
|
Write-Log -Message "Fetching List of Locations"
|
|
$LocationList = ((az account list-locations) | ConvertFrom-Json)
|
|
Write-Log -Message "List of Locations:-`n$($locationList | ConvertTo-Json -Compress)"
|
|
|
|
if(!$LocationName) {
|
|
Write-Host "$(az account list-locations --output table --query "[].{Name:name}" | Out-String)`n"
|
|
$LocationName = Read-Host 'Enter Location From Above List for Resource Provisioning'
|
|
#trimming the input for empty spaces, if any
|
|
$LocationName = $LocationName.Trim()
|
|
}
|
|
Write-Log -Message "User Provided Location Name: $LocationName"
|
|
|
|
$ValidLocation = $LocationList | Where-Object { $_.name -ieq $LocationName }
|
|
if(!$ValidLocation) {
|
|
throw "Invalid Location Name Entered."
|
|
}
|
|
#endregion
|
|
|
|
#region Create New App Registration in AzureAD
|
|
Write-Title 'STEP #4 - Registering Azure Active Directory App'
|
|
|
|
Write-Log -Message "Creating AAD App with Name: $ResourceGroupName"
|
|
$appinfo=$(az ad app create --display-name $ResourceGroupName) | ConvertFrom-Json;
|
|
if(!$appinfo) {
|
|
throw "Encountered an Error while creating AAD App"
|
|
}
|
|
# $identifierURI = "api://$($appinfo.appId)";
|
|
# Write-Log -Message "Updating Identifier URI's in AAD App to: [ api://$($appinfo.appId) ]"
|
|
# $appUpdateOp = az ad app update --id $appinfo.appId --identifier-uris $identifierURI;
|
|
# Intentionally not catching an exception here since the app update commands behavior (output) is different from others
|
|
|
|
# Write-Log -Message "Updating App so as to add MS Graph -> User Profile -> Read Permissions to the AAD App"
|
|
# $GraphAPIId = '00000003-0000-0000-c000-000000000000'
|
|
# $GraphAPIPermissionId = 'e1fe6dd8-ba31-4d61-89e7-88639da4683d=Scope'
|
|
# $appPermissionAddOp = az ad app permission add --id $appinfo.appId --api $GraphAPIId --api-permissions $GraphAPIPermissionId
|
|
#Intentionally not catching an exception here
|
|
|
|
Write-Host 'App Created Successfully'
|
|
#endregion
|
|
|
|
#region Create New Resource Group in above Region
|
|
Write-Title 'STEP #5 - Creating Resource Group'
|
|
|
|
Write-Log -Message "Creating Resource Group with Name: $ResourceGroupName at Location: $LocationName"
|
|
$resourceGroupCreationOp = az group create -l $LocationName -n $ResourceGroupName
|
|
if(!$resourceGroupCreationOp) {
|
|
$ex = @(
|
|
"Error while creating Resource Group with Name : [ $ResourceGroupName ] at Location: [ $LocationName ]."
|
|
"One Reason could be that a Resource Group with same name but different location already exists in your Subscription."
|
|
"Delete the other Resource Group and run this script again."
|
|
) -join ' '
|
|
throw $ex
|
|
}
|
|
|
|
Write-Host 'Resource Group Created Successfully'
|
|
#endregion
|
|
|
|
#region Provision Resources inside Resource Group on Azure using ARM template
|
|
Write-Title 'STEP #6 - Creating Resources in Azure'
|
|
|
|
$userObjectId = az ad signed-in-user show --query objectId
|
|
|
|
$templateFileName = "azuredeploy.json"
|
|
$deploymentName = "Deployment-$ExecutionStartTime"
|
|
Write-Log -Message "Deploying ARM Template to Azure inside ResourceGroup: $ResourceGroupName with DeploymentName: $deploymentName, TemplateFile: $templateFileName, AppClientId: $($appinfo.appId), IdentifiedURI: $($appinfo.identifierUris)"
|
|
$deploymentOutput = (az deployment group create --resource-group $ResourceGroupName --name $deploymentName --template-file $templateFileName --parameters objectId=$($userObjectId)) | ConvertFrom-Json;
|
|
if(!$deploymentOutput) {
|
|
throw "Encountered an Error while deploying to Azure"
|
|
}
|
|
Write-Host 'Resource Creation in Azure Completed Successfully'
|
|
|
|
Write-Title 'Step #7 - Updating KeyVault with LTI 1.3 Key'
|
|
|
|
function Update-LtiFunctionAppSettings([string]$ResourceGroupName, [string]$FunctionAppName, [hashtable]$AppSettings) {
|
|
Write-Log -Message "Updating App Settings for Function App [ $FunctionAppName ]: -"
|
|
foreach ($it in $AppSettings.GetEnumerator()) {
|
|
Write-Log -Message "`t[ $($it.Name) ] = [ $($it.Value) ]"
|
|
az functionapp config appsettings set --resource-group $ResourceGroupName --name $FunctionAppName --settings "$($it.Name)=$($it.Value)"
|
|
}
|
|
}
|
|
|
|
#Creating EdnaLiteDevKey in keyVault and Updating the Config Entry EdnaLiteDevKey in the Function Config
|
|
$keyCreationOp = (az keyvault key create --vault-name $deploymentOutput.properties.outputs.KeyVaultName.value --name EdnaLiteDevKey --protection software) | ConvertFrom-Json;
|
|
if(!$keyCreationOp) {
|
|
throw "Encountered an Error while creating Key in keyVault"
|
|
}
|
|
$KeyVaultLink = $keyCreationOp.key.kid
|
|
$EdnaKeyString = @{ "EdnaKeyString"="$KeyVaultLink" }
|
|
$FunctionAppUpdateOp = Update-LtiFunctionAppSettings $ResourceGroupName $deploymentOutput.properties.outputs.FunctionAppName.value $EdnaKeyString
|
|
|
|
Write-Host 'Key Creation in KeyVault Completed Successfully'
|
|
|
|
Write-Title 'Step #8 - Enabling Static Website Container'
|
|
|
|
#Creating a Container in Static Website
|
|
$containerEnableOp = az storage blob service-properties update --account-name $deploymentOutput.properties.outputs.StorageName.value --static-website --404-document index.html --index-document index.html --only-show-errors
|
|
if(!$containerEnableOp) {
|
|
throw "Encountered an Error while creating Container to host Static Website"
|
|
}
|
|
Write-Host 'Static Website Container Enabled Successfully'
|
|
|
|
Write-Title 'STEP #9 - Updating AAD App'
|
|
|
|
$AppRedirectUrl = -join($deploymentOutput.properties.outputs.MainUrl.value, "/login")
|
|
Write-Log -Message "Updating App with ID: $($appinfo.appId) to Redirect URL: $AppRedirectUrl"
|
|
# "az ad" does not support single page applications, using "az rest" as suggested in
|
|
# https://github.com/Azure/azure-cli/issues/14880
|
|
az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$($appinfo.objectId)" --headers='Content-Type=application/json' --body="{\`"spa\`":{\`"redirectUris\`":[\`"$AppRedirectUrl\`"]}}"
|
|
# $appUpdateRedirectUrlOp = az ad app update --id $appinfo.appId --reply-urls $AppRedirectUrl --oauth2-allow-implicit-flow true
|
|
#Intentionally not catching an exception here since the app update commands behavior (output) is different from others
|
|
|
|
Write-Host 'App Update Completed Successfully'
|
|
#endregion
|
|
|
|
#region Build and Publish Function Apps
|
|
. .\Install-Backend.ps1
|
|
Write-Title "STEP #10 - Installing the backend"
|
|
|
|
$BackendParams = @{
|
|
SourceRoot="../api";
|
|
ResourceGroupName=$ResourceGroupName;
|
|
FunctionAppName=$deploymentOutput.properties.outputs.FunctionAppName.value;
|
|
}
|
|
Install-Backend @BackendParams
|
|
#endregion
|
|
|
|
#region Build and Publish Client Artifacts
|
|
. .\Install-Client.ps1
|
|
Write-Title "STEP #11 - Updating client's .env.production file"
|
|
|
|
$ClientUpdateConfigParams = @{
|
|
ConfigPath="../client/.env.production";
|
|
ClientId=$appinfo.appId;
|
|
}
|
|
Update-ClientConfig @ClientUpdateConfigParams
|
|
|
|
Write-Title 'STEP #12 - Installing the client'
|
|
|
|
$ClientInstallParams = @{
|
|
SourceRoot="../client";
|
|
StaticWebsiteStorageAccount=$deploymentOutput.properties.outputs.StorageName.value
|
|
}
|
|
Install-Client @ClientInstallParams
|
|
#endregion
|
|
|
|
Write-Title "TOOL REGISTRATION URL (Please Copy, Required for Next Steps) -> $($deploymentOutput.properties.outputs.MainUrl.value)/spa/platform"
|
|
|
|
Write-Title '======== Successfully Deployed Resources to Azure ==========='
|
|
|
|
Write-Log -Message "Deployment Complete"
|
|
}
|
|
catch {
|
|
$Message = 'Error occurred while executing the Script. Please report the bug on Github (along with Error Message & Logs)'
|
|
Write-Log -Message $Message -ErrorRecord $_
|
|
throw $_
|
|
}
|
|
finally {
|
|
Stop-Transcript
|
|
$exit = Read-Host 'Press any Key to Exit'
|
|
}
|
|
} |