diff --git a/BotProject/CSharp/Scripts/create.ps1 b/BotProject/CSharp/Scripts/create.ps1 new file mode 100644 index 0000000000..bd30ddf40c --- /dev/null +++ b/BotProject/CSharp/Scripts/create.ps1 @@ -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 +} \ No newline at end of file diff --git a/BotProject/CSharp/Scripts/deploy.ps1 b/BotProject/CSharp/Scripts/deploy.ps1 new file mode 100644 index 0000000000..1efad16116 --- /dev/null +++ b/BotProject/CSharp/Scripts/deploy.ps1 @@ -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 +} \ No newline at end of file