This repository was archived by the owner on Jul 9, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 375
Deployment CLI tools #737
Merged
Merged
Deployment CLI tools #737
Changes from 9 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
0137159
add deploy scripts
9fe9877
remove commented line.
ef098ba
rename the script and add environment.
ae3d864
add output
c8c1eb4
Merge branch 'master' into qika/deployment
luhan2017 19eb12b
add luis config.
f6bb7c9
Merge branch 'qika/deployment' of https://github.com/microsoft/BotFra…
b467113
add custom config from PR #719
3f40285
minor fix for comments.
f3813c0
Merge branch 'master' into qika/deployment
7e98cb3
Merge branch 'master' into qika/deployment
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| Param( | ||
| [string] $name, | ||
| [string] $location, | ||
| [string] $appId, | ||
| [string] $appPassword, | ||
| [string] $environment, | ||
| [string] $projDir = $(Get-Location), | ||
| [string] $logFile = $(Join-Path $PSScriptRoot .. "create_log.txt") | ||
| ) | ||
|
|
||
| if ($PSVersionTable.PSVersion.Major -lt 6){ | ||
| Write-Host "! Powershell 6 is required, current version is $($PSVersionTable.PSVersion.Major), please refer following documents for help." | ||
| Write-Host "For Windows - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6" | ||
| Write-Host "For Mac - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-6" | ||
| Break | ||
| } | ||
|
|
||
| # Reset log file | ||
| if (Test-Path $logFile) { | ||
| Clear-Content $logFile -Force | Out-Null | ||
| } | ||
| else { | ||
| New-Item -Path $logFile | Out-Null | ||
| } | ||
|
|
||
| if (-not (Test-Path (Join-Path $projDir 'appsettings.json'))) | ||
| { | ||
| Write-Host "! Could not find an 'appsettings.json' file in the current directory." -ForegroundColor DarkRed | ||
| Write-Host "+ Please re-run this script from your project directory." -ForegroundColor Magenta | ||
| Break | ||
| } | ||
|
|
||
| # Get mandatory parameters | ||
| if (-not $name) { | ||
| $name = Read-Host "? Bot Name (used as default name for resource group and deployed resources)" | ||
| } | ||
|
|
||
| if (-not $environment) | ||
| { | ||
| $environment = Read-Host "? Environment Name (single word, all lowercase)" | ||
| $environment = $environment.ToLower().Split(" ") | Select-Object -First 1 | ||
| } | ||
|
|
||
| if (-not $location) { | ||
| $location = Read-Host "? Azure resource group region" | ||
| } | ||
|
|
||
| if (-not $appPassword) { | ||
| $appPassword = Read-Host "? Password for MSA app registration (must be at least 16 characters long, contain at least 1 special character, and contain at least 1 numeric character)" | ||
| } | ||
|
|
||
| if (-not $appId) { | ||
| # Create app registration | ||
| $app = (az ad app create ` | ||
| --display-name $name ` | ||
| --password `"$($appPassword)`" ` | ||
| --available-to-other-tenants ` | ||
| --reply-urls 'https://token.botframework.com/.auth/web/redirect' ` | ||
| --output json) | ||
|
|
||
| # Retrieve AppId | ||
| if ($app) { | ||
| $appId = ($app | ConvertFrom-Json) | Select-Object -ExpandProperty appId | ||
| } | ||
|
|
||
| if(-not $appId) { | ||
| Write-Host "! Could not provision Microsoft App Registration automatically. Review the log for more information." -ForegroundColor DarkRed | ||
| Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed | ||
| Write-Host "+ Provision an app manually in the Azure Portal, then try again providing the -appId and -appPassword arguments. See https://aka.ms/vamanualappcreation for more information." -ForegroundColor Magenta | ||
| Break | ||
| } | ||
| } | ||
|
|
||
| $resourceGroup = "$name-$environment" | ||
| $servicePlanName = "$name-$environment" | ||
|
|
||
| # Get timestamp | ||
| $timestamp = Get-Date -f MMddyyyyHHmmss | ||
|
|
||
| # Create resource group | ||
| Write-Host "> Creating resource group ..." | ||
| (az group create --name $resourceGroup --location $location --output json) 2>> $logFile | Out-Null | ||
|
|
||
| # Deploy Azure services | ||
| Write-Host "> Validating Azure deployment ..." | ||
| $validation = az group deployment validate ` | ||
| --resource-group $resourcegroup ` | ||
| --template-file "$(Join-Path $PSScriptRoot '..' 'DeploymentTemplates' 'template-with-preexisting-rg.json')" ` | ||
| --parameters appId=$appId appSecret="`"$($appPassword)`"" newAppServicePlanName=$servicePlanName appServicePlanLocation=$location botId=$name ` | ||
| --output json | ||
|
|
||
| if ($validation) { | ||
| $validation >> $logFile | ||
| $validation = $validation | ConvertFrom-Json | ||
|
|
||
| if (-not $validation.error) { | ||
| Write-Host "> Deploying Azure services (this could take a while)..." -ForegroundColor Yellow | ||
| $deployment = az group deployment create ` | ||
| --name $timestamp ` | ||
| --resource-group $resourceGroup ` | ||
| --template-file "$(Join-Path $PSScriptRoot '..' 'DeploymentTemplates' 'template-with-preexisting-rg.json')" ` | ||
| --parameters appId=$appId appSecret="`"$($appPassword)`"" newAppServicePlanName=$servicePlanName appServicePlanLocation=$location botId=$name ` | ||
| --output json | ||
| } | ||
| else { | ||
| Write-Host "! Template is not valid with provided parameters. Review the log for more information." -ForegroundColor DarkRed | ||
| Write-Host "! Error: $($validation.error.message)" -ForegroundColor DarkRed | ||
| Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed | ||
| Write-Host "+ To delete this resource group, run 'az group delete -g $($resourceGroup) --no-wait'" -ForegroundColor Magenta | ||
| Break | ||
| } | ||
| } | ||
|
|
||
|
|
||
| # Get deployment outputs | ||
| $outputs = az group deployment show ` | ||
| --name $timestamp ` | ||
| --resource-group $resourceGroup ` | ||
| --output json 2>> $logFile | ||
|
|
||
| # If it succeeded then we perform the remainder of the steps | ||
| if ($outputs) | ||
| { | ||
| # Log and convert to JSON | ||
| $outputs >> $logFile | ||
| $outputs = $outputs | ConvertFrom-Json | ||
| $outputMap = @{} | ||
| $outputs.PSObject.Properties | Foreach-Object { $outputMap[$_.Name] = $_.Value } | ||
|
|
||
| # Update appsettings.json | ||
| Write-Host "> Updating appsettings.json ..." | ||
| if (Test-Path $(Join-Path $projDir appsettings.json)) { | ||
| $settings = Get-Content $(Join-Path $projDir appsettings.json) | ConvertFrom-Json | ||
| } | ||
| else { | ||
| $settings = New-Object PSObject | ||
| } | ||
|
|
||
| $settings | Add-Member -Type NoteProperty -Force -Name 'microsoftAppId' -Value $appId | ||
| $settings | Add-Member -Type NoteProperty -Force -Name 'microsoftAppPassword' -Value $appPassword | ||
| $settings | Add-Member -Type NoteProperty -Force -Name 'bot' -Value "RunningInstance" | ||
|
|
||
| foreach ($key in $outputMap.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $outputMap[$key].value } | ||
| $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $projDir appsettings.json) | ||
|
|
||
| Write-Host "> Done." | ||
| Write-Host "- App Id: $appId" | ||
| Write-Host "- App Password: $appPassword" | ||
| Write-Host "- Resource Group: $resourceGroup" | ||
| Write-Host "- ServicePlan: $servicePlanName" | ||
| Write-Host "- Bot Name: $name" | ||
| Write-Host "- Web App Name : $name" | ||
| } | ||
| else | ||
| { | ||
| # Check for failed deployments | ||
| $operations = az group deployment operation list -g $resourceGroup -n $timestamp --output json 2>> $logFile | Out-Null | ||
|
|
||
| if ($operations) { | ||
| $operations = $operations | ConvertFrom-Json | ||
| $failedOperations = $operations | Where { $_.properties.statusmessage.error -ne $null } | ||
| if ($failedOperations) { | ||
| foreach ($operation in $failedOperations) { | ||
| switch ($operation.properties.statusmessage.error.code) { | ||
| "MissingRegistrationForLocation" { | ||
| Write-Host "! Deployment failed for resource of type $($operation.properties.targetResource.resourceType). This resource is not avaliable in the location provided." -ForegroundColor DarkRed | ||
| Write-Host "+ Update the .\Deployment\Resources\parameters.template.json file with a valid region for this resource and provide the file path in the -parametersFile parameter." -ForegroundColor Magenta | ||
| } | ||
| default { | ||
| Write-Host "! Deployment failed for resource of type $($operation.properties.targetResource.resourceType)." | ||
| Write-Host "! Code: $($operation.properties.statusMessage.error.code)." | ||
| Write-Host "! Message: $($operation.properties.statusMessage.error.message)." | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| Write-Host "! Deployment failed. Please refer to the log file for more information." -ForegroundColor DarkRed | ||
| Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed | ||
| } | ||
|
|
||
| Write-Host "+ To delete this resource group, run 'az group delete -g $($resourceGroup) --no-wait'" -ForegroundColor Magenta | ||
| Break | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| Param( | ||
| [string] $name, | ||
| [string] $environment, | ||
| [string] $luisAuthoringKey, | ||
| [string] $luisAuthoringRegion, | ||
| [string] $projFolder = $(Get-Location), | ||
| [string] $botPath, | ||
| [string] $logFile = $(Join-Path $PSScriptRoot .. "deploy_log.txt") | ||
| ) | ||
|
|
||
| if ($PSVersionTable.PSVersion.Major -lt 6){ | ||
| Write-Host "! Powershell 6 is required, current version is $($PSVersionTable.PSVersion.Major), please refer following documents for help." | ||
| Write-Host "For Windows - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6" | ||
| Write-Host "For Mac - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-6" | ||
| Break | ||
| } | ||
|
|
||
| # Get mandatory parameters | ||
| if (-not $name) { | ||
| $name = Read-Host "? Bot Web App Name" | ||
| } | ||
|
|
||
| if (-not $environment) | ||
| { | ||
| $environment = Read-Host "? Environment Name (single word, all lowercase)" | ||
| $environment = $environment.ToLower().Split(" ") | Select-Object -First 1 | ||
| } | ||
|
|
||
| if (-not $botPath) { | ||
| $botPath = Read-Host "? The Reletive Path Of Bot" | ||
| } | ||
|
|
||
|
|
||
| # Reset log file | ||
| if (Test-Path $logFile) { | ||
| Clear-Content $logFile -Force | Out-Null | ||
| } | ||
| else { | ||
| New-Item -Path $logFile | Out-Null | ||
| } | ||
|
|
||
| # Check for existing deployment files | ||
| if (-not (Test-Path (Join-Path $projFolder '.deployment'))) { | ||
| # Add needed deployment files for az | ||
| az bot prepare-deploy --lang Csharp --code-dir $projFolder --proj-file-path BotProject.csproj --output json | Out-Null | ||
| } | ||
|
|
||
| # Delete src zip, if it exists | ||
| $zipPath = $(Join-Path $projFolder 'code.zip') | ||
| if (Test-Path $zipPath) { | ||
| Remove-Item $zipPath -Force | Out-Null | ||
| } | ||
|
|
||
| # Perform dotnet publish step ahead of zipping up | ||
| $publishFolder = $(Join-Path $projFolder 'bin\Release\netcoreapp2.2') | ||
| dotnet publish -c release -o $publishFolder -v q > $logFile | ||
|
|
||
| # Copy bot files to running folder | ||
| $remoteBotPath = $(Join-Path $publishFolder "RunningInstance") | ||
| Remove-Item $remoteBotPath -Recurse -ErrorAction Ignore | ||
| Copy-Item -Path $botPath -Recurse -Destination $remoteBotPath -Container -Force | ||
|
|
||
| # Merge from custom config files | ||
| $customConfigFiles = Get-ChildItem -Path $remoteBotPath -Include "appsettings.json" -Recurse -Force | ||
| if ($customConfigFiles) | ||
| { | ||
| if (Test-Path $(Join-Path $publishFolder appsettings.json)) { | ||
| $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json | ||
| } | ||
| else { | ||
| $settings = New-Object PSObject | ||
| } | ||
|
|
||
| $customConfig = @{} | ||
| $customSetting = Get-Content $customConfigFiles.FullName | ConvertFrom-Json | ||
| $customSetting.PSObject.Properties | Foreach-Object { $customConfig[$_.Name] = $_.Value } | ||
| foreach ($key in $customConfig.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $customConfig[$key] } | ||
|
|
||
| $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) | ||
| } | ||
|
|
||
| # Add Luis Config to appsettings | ||
| if ($luisAuthoringKey -and $luisAuthoringRegion) | ||
| { | ||
| # change setting file in publish folder | ||
| if (Test-Path $(Join-Path $publishFolder appsettings.json)) { | ||
| $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json | ||
| } | ||
| else { | ||
| $settings = New-Object PSObject | ||
| } | ||
|
|
||
| $luisConfigFiles = Get-ChildItem -Path $publishFolder -Include "luis.settings*" -Recurse -Force | ||
|
|
||
| $luisAppIds = @{} | ||
|
|
||
| foreach ($luisConfigFile in $luisConfigFiles) | ||
| { | ||
| $luisSetting = Get-Content $luisConfigFile.FullName | ConvertFrom-Json | ||
| $luis = $luisSetting.luis | ||
| $luis.PSObject.Properties | Foreach-Object { $luisAppIds[$_.Name] = $_.Value } | ||
| } | ||
|
|
||
| $luisEndpoint = "https://$luisAuthoringRegion.api.cognitive.microsoft.com" | ||
|
|
||
| $luisConfig = @{} | ||
|
|
||
| $luisConfig["endpointKey"] = $luisAuthoringKey | ||
| $luisConfig["endpoint"] = $luisEndpoint | ||
|
|
||
| foreach ($key in $luisAppIds.Keys) { $luisConfig[$key] = $luisAppIds[$key] } | ||
|
|
||
| $settings | Add-Member -Type NoteProperty -Force -Name 'luis' -Value $luisConfig | ||
|
|
||
| $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) | ||
| } | ||
|
|
||
| $resourceGroup = "$name-$environment" | ||
|
|
||
| if($?) | ||
| { | ||
| # Compress source code | ||
| Get-ChildItem -Path "$($publishFolder)" | Compress-Archive -DestinationPath "$($zipPath)" -Force | Out-Null | ||
|
|
||
| # Publish zip to Azure | ||
| Write-Host "> Publishing to Azure ..." -ForegroundColor Green | ||
| (az webapp deployment source config-zip ` | ||
| --resource-group $resourceGroup ` | ||
| --name $name ` | ||
| --src $zipPath ` | ||
| --output json) 2>> $logFile | Out-Null | ||
| } | ||
| else | ||
| { | ||
| Write-Host "! Could not deploy automatically to Azure. Review the log for more information." -ForegroundColor DarkRed | ||
| Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wenyi is working on editing environment variables in composer and post them to bot project, how could those settings passed to this deployment script??
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now the script search the appsettings.json file in bot and merge with the main appsettings.json in BotProject root folder.