From 8038e30e3bb04e6b86f2e461f6ba4f3544d36c8d Mon Sep 17 00:00:00 2001 From: Christian Glessner Date: Thu, 10 Sep 2020 18:47:20 +0200 Subject: [PATCH 1/3] Fixes various issue in the web app deployment script deploy improvements fixes missing empty models; removes accidentally committed yarn.lock --- runtime/dotnet/azurewebapp/Scripts/deploy.ps1 | 106 ++++++------------ 1 file changed, 37 insertions(+), 69 deletions(-) diff --git a/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 b/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 index 3f510dfb8f..71d2abd197 100644 --- a/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 +++ b/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 @@ -56,22 +56,17 @@ if (Test-Path $zipPath) { Remove-Item $zipPath -Force | Out-Null } -# Init user secret id -dotnet user-secrets init - # Perform dotnet publish step ahead of zipping up $publishFolder = $(Join-Path $projFolder 'bin\Release\netcoreapp3.1') dotnet publish -c release -o $publishFolder -v q > $logFile - # Copy bot files to running folder $remoteBotPath = $(Join-Path $publishFolder "ComposerDialogs") Remove-Item $remoteBotPath -Recurse -ErrorAction Ignore - if (-not $botPath) { # If don't provide bot path, then try to copy all dialogs except the runtime folder in parent folder to the publishing folder (bin\Realse\ Folder) - $botPath = '..' + $botPath = '../..' } $botPath = $(Join-Path $botPath '*') @@ -79,85 +74,76 @@ Write-Host "Publishing dialogs from external bot project: $($botPath)" Copy-Item -Path (Get-Item -Path $botPath -Exclude ('runtime', 'generated')).FullName -Destination $remoteBotPath -Recurse -Force -Container # Try to get luis config from appsettings -$settings = Get-Content $(Join-Path $projFolder appsettings.deployment.json) | ConvertFrom-Json +$settingsPath = $(Join-Path $remoteBotPath settings appsettings.json) +$settings = Get-Content $settingsPath | ConvertFrom-Json $luisSettings = $settings.luis if (-not $luisAuthoringKey) { $luisAuthoringKey = $luisSettings.authoringKey } -if (-not $luisEndpointKey) { - $luisEndpointKey = $luisSettings.endpointKey -} - if (-not $luisAuthoringRegion) { $luisAuthoringRegion = $luisSettings.region } +# set feature configuration +$featureConfig = @{ } +if ($settings.feature) { + $featureConfig = $settings.feature +} +else { + # Enable all features to true by default + $featureConfig["UseTelementryLoggerMiddleware"] = $true + $featureConfig["UseTranscriptLoggerMiddleware"] = $true + $featureConfig["UseShowTypingMiddleware"] = $true + $featureConfig["UseInspectionMiddleware"] = $true + $featureConfig["UseCosmosDb"] = $true +} + # Add Luis Config to appsettings if ($luisAuthoringKey -and $luisAuthoringRegion) { - Set-Location -Path $remoteBotPath - $models = Get-ChildItem $remoteBotPath -Recurse -Filter "*.lu" | Resolve-Path -Relative - $noneEmptyModels = [System.Collections.ArrayList]@() - - foreach ($model in $models) { - $stringContent = Get-Content $model | Out-String - if ($stringContent.Length -gt 0) { - $noneEmptyModels.Add($model) - } - } + $models = Get-ChildItem $remoteBotPath -Recurse -Filter "*.lu" | Resolve-Path -Relative # Generate Luconfig.json file $luconfigjson = @{ "name" = $name; "defaultLanguage" = $language; - "models" = $noneEmptyModels + "models" = $models } - - $luString = $noneEmptyModels | Out-String + + $luString = $models | Out-String Write-Host $luString $luconfigjson | ConvertTo-Json -Depth 100 | Out-File $(Join-Path $remoteBotPath luconfig.json) # Execute bf luis:build command if (Get-Command bf -errorAction SilentlyContinue) { - $customizedSettings = Get-Content $(Join-Path $remoteBotPath settings appsettings.json) | ConvertFrom-Json - $customizedEnv = $customizedSettings.luis.environment - - # create generated folder if not exists + # create generated folder if not if (!(Test-Path generated)) { - New-Item -ItemType Directory -Force -Path generated + $null = New-Item -ItemType Directory -Force -Path generated } - - bf luis:build --luConfig $(Join-Path $remoteBotPath luconfig.json) --botName $name --authoringKey $luisAuthoringKey --dialog --out .\generated --suffix $customizedEnv -f --region $luisAuthoringRegion + bf luis:build --luConfig $(Join-Path $remoteBotPath luconfig.json) --botName $name --authoringKey $luisAuthoringKey --dialog crosstrained --out ./generated --suffix $environment -f --region $luisAuthoringRegion } else { Write-Host "bf luis:build does not exist, use the following command to install:" - Write-Host "1. npm config set registry https://botbuilder.myget.org/F/botframework-cli/npm/" - Write-Host "2. npm install -g @microsoft/botframework-cli/4.9.0-preview.121555" - Write-Host "3. npm config set registry http://registry.npmjs.org" + Write-Host "npm install -g @microsoft/botframework-cli" Break } - + if ($?) { Write-Host "lubuild succeeded" } else { Write-Host "lubuild failed, please verify your luis models." - Break + Break } Set-Location -Path $projFolder - # change setting file in publish folder - if (Test-Path $(Join-Path $publishFolder appsettings.deployment.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.deployment.json) | ConvertFrom-Json - } - else { - $settings = New-Object PSObject - } + # clear the settings; we don't want to unintentionally copy secrets to the remote web app + $settings = New-Object PSObject $luisConfigFiles = Get-ChildItem -Path $publishFolder -Include "luis.settings*" -Recurse -Force @@ -172,16 +158,13 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { $luisEndpoint = "https://$luisAuthoringRegion.api.cognitive.microsoft.com" $luisConfig = @{ } - + $luisConfig["endpoint"] = $luisEndpoint - $luisConfig["endpointKey"] = $luisEndpointKey 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.deployment.json) - $tokenResponse = (az account get-access-token) | ConvertFrom-Json $token = $tokenResponse.accessToken @@ -226,27 +209,12 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { } } -# Enable all features to true by default -$featureConfig = @{ } -$featureConfig["UseTelementryLoggerMiddleware"] = $true -$featureConfig["UseTranscriptLoggerMiddleware"] = $true -$featureConfig["UseShowTypingMiddleware"] = $true -$featureConfig["UseInspectionMiddleware"] = $true -$featureConfig["UseCosmosDb"] = $true - -if (Test-Path $(Join-Path $publishFolder appsettings.deployment.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.deployment.json) | ConvertFrom-Json -} -else { - $settings = New-Object PSObject -} - $settings | Add-Member -Type NoteProperty -Force -Name 'feature' -Value $featureConfig -$settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.deployment.json) +$settings | ConvertTo-Json -depth 100 | Out-File $settingsPath $resourceGroup = "$name-$environment" -if ($?) { +if ($?) { # Compress source code Get-ChildItem -Path "$($publishFolder)" | Compress-Archive -DestinationPath "$($zipPath)" -Force | Out-Null @@ -257,7 +225,7 @@ if ($?) { --name "$name-$environment" ` --src $zipPath ` --output json) 2>> $logFile - + if ($deployment) { Write-Host "Publish Success" } @@ -265,8 +233,8 @@ if ($?) { Write-Host "! Deploy failed. Review the log for more information." -ForegroundColor DarkRed Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed } -} -else { +} +else { Write-Host "! Could not deploy automatically to Azure. Review the log for more information." -ForegroundColor DarkRed - Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed -} + Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed +} From 259b3941fb451d7a60f37771112c88c97d799727 Mon Sep 17 00:00:00 2001 From: Christian Glessner Date: Thu, 10 Sep 2020 18:50:30 +0200 Subject: [PATCH 2/3] Deletes obsolete scripts --- .../azurewebapp/Scripts/build_runtime.ps1 | 17 -- .../azurewebapp/Scripts/build_runtime.sh | 11 - runtime/dotnet/azurewebapp/Scripts/create.ps1 | 211 ------------------ 3 files changed, 239 deletions(-) delete mode 100644 runtime/dotnet/azurewebapp/Scripts/build_runtime.ps1 delete mode 100644 runtime/dotnet/azurewebapp/Scripts/build_runtime.sh delete mode 100644 runtime/dotnet/azurewebapp/Scripts/create.ps1 diff --git a/runtime/dotnet/azurewebapp/Scripts/build_runtime.ps1 b/runtime/dotnet/azurewebapp/Scripts/build_runtime.ps1 deleted file mode 100644 index 902d302c47..0000000000 --- a/runtime/dotnet/azurewebapp/Scripts/build_runtime.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -if ((dotnet --version) -lt '3.1.0') { - throw "! dotnet core 3.1 is required, please refer following documents for help. https://dotnet.microsoft.com/download/dotnet-core/3.1" - Break -} - -# This command need dotnet core more than 3.0 -dotnet user-secrets init - -# Merge all streams into stdout -$result = dotnet build *>&1 -# Evaluate success/failure -if($LASTEXITCODE -ne 0) -{ - # Failed, you can reconstruct stderr strings with: - $ErrorString = $result -join [System.Environment]::NewLine - throw $ErrorString -} diff --git a/runtime/dotnet/azurewebapp/Scripts/build_runtime.sh b/runtime/dotnet/azurewebapp/Scripts/build_runtime.sh deleted file mode 100644 index 51e0b0ae12..0000000000 --- a/runtime/dotnet/azurewebapp/Scripts/build_runtime.sh +++ /dev/null @@ -1,11 +0,0 @@ -versionString=`dotnet --version` -function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } -if version_lt $versionString "3.1.0"; -then - echo "! dotnet core 3.1 is required, please refer following documents for help. -https://dotnet.microsoft.com/download/dotnet-core/3.1" - exit 1 -else - dotnet user-secrets init - dotnet build -fi diff --git a/runtime/dotnet/azurewebapp/Scripts/create.ps1 b/runtime/dotnet/azurewebapp/Scripts/create.ps1 deleted file mode 100644 index d3ac6cac73..0000000000 --- a/runtime/dotnet/azurewebapp/Scripts/create.ps1 +++ /dev/null @@ -1,211 +0,0 @@ -Param( - [string] $name, - [string] $location, - [string] $appId, - [string] $appPassword, - [string] $environment, - [string] $luisAuthoringKey, - [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.deployment.json'))) -{ - Write-Host "! Could not find an 'appsettings.deployment.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 - } -} - -$shouldCreateAuthoringResource = $true - -# Use pre-exsisting luis authoring key -if ($luisAuthoringKey) { - $shouldCreateAuthoringResource = $false -} - -$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)`"" appServicePlanLocation=$location botId=$name shouldCreateAuthoringResource=$shouldCreateAuthoringResource luisAuthoringKey=$luisAuthoringKey ` - --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)`"" appServicePlanLocation=$location botId=$name shouldCreateAuthoringResource=$shouldCreateAuthoringResource luisAuthoringKey=$luisAuthoringKey ` - --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 - - if ($validation.error.details -and $validation.error.details[0].code -eq "CanNotCreateMultipleFreeAccounts") - { - Write-Host "! The subscription is exceeding the maximum number of allowed LuisAuthoringAccounts. You already have a luis authoring resource created, please get your luis authoring key and retry with the following command:" -ForegroundColor DarkRed - Write-Host "pwsh ./Scripts/create.ps1 -name $name -environment $environment -location $location -appPassword $appPassword -luisAuthoringKey [YourLuisAuthoringKey]" -ForegroundColor Green - } - - 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 - if ($outputs.properties.error) { - Write-Host "! Deployment failed. Review the log for more information." -ForegroundColor DarkRed - Write-Host "! Error: $($outputs.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 - } - - $outputs = $outputs.properties.outputs - $outputMap = @{} - $outputs.PSObject.Properties | Foreach-Object { $outputMap[$_.Name] = $_.Value } - - # Update appsettings.deployment.json - Write-Host "> Updating appsettings.deployment.json ..." - if (Test-Path $(Join-Path $projDir appsettings.deployment.json)) { - $settings = Get-Content $(Join-Path $projDir appsettings.deployment.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 "ComposerDialogs" - - 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.deployment.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 -} From 1aa9d6baff6cd5556e46c0c4c25a605e09f33f09 Mon Sep 17 00:00:00 2001 From: Christian Glessner Date: Thu, 10 Sep 2020 18:53:46 +0200 Subject: [PATCH 3/3] Optimises deployment script for CI/CD pipeline; readme --- runtime/dotnet/azurewebapp/README.md | 24 +++++++++---------- runtime/dotnet/azurewebapp/Scripts/deploy.ps1 | 23 +++++++++--------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/runtime/dotnet/azurewebapp/README.md b/runtime/dotnet/azurewebapp/README.md index 356528b3d7..0139518b13 100644 --- a/runtime/dotnet/azurewebapp/README.md +++ b/runtime/dotnet/azurewebapp/README.md @@ -1,21 +1,19 @@ ## Bot Project + Bot project is the launcher project for the bots written in declarative form (JSON), using the Composer, for the Bot Framework SDK. -## Instructions for setting up the Bot Project runtime -The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch it from the emulator, you need to make sure you can run the bot. +### CI/CD Deployment + +You can deploy your bot to an Azure Web App with the following script from an Azure pipeline or GitHub workflow using an [Azure CLI task](https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-cli). -### Prerequisite: -* Install .Netcore 3.1 +```bash +./scripts/deploy.ps1 -name my-bot -environment prod -luisAuthoringKey XXXXXXXXX -luisAuthoringRegion westeurope +``` -### Commands: +The Azure CLI task needs contribution permission to the corresponding resource group. Follow this [article](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/connect-to-azure) to setup a service connection between Azure DevOps and your Azure Subscription. -* from root folder -* cd templates/dotnet/Microsoft.BotFramework.Composer.WebAppTemplate -* dotnet user-secrets init // init the user secret id -* dotnet build // build -* dotnet run // start the bot -* It will start a web server and listening at http://localhost:3979. +Initially you can provision the resources with the provisionComposer.js script, which you can find in the boot root folder under scripts. -### Test bot -* You can set you emulator to connect to http://localhost:3979/api/messages. +The environment, bot name and authoring region must match the arguments, that you used for for the provisioning with the provisionComposer.js script. +For security reasons we don't deploy any settings or secrets from the bot project. Please ensure that required settings for your bit are configured in the [Azure Web App configuration](https://docs.microsoft.com/en-us/azure/app-service/configure-common), for example "MicrosoftAppPassword", "luis\_\_endpointKey", "cosmosDB\_\_authKey", ... diff --git a/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 b/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 index 71d2abd197..bb6dc8aaaa 100644 --- a/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 +++ b/runtime/dotnet/azurewebapp/Scripts/deploy.ps1 @@ -118,20 +118,22 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { $luconfigjson | ConvertTo-Json -Depth 100 | Out-File $(Join-Path $remoteBotPath luconfig.json) - # Execute bf luis:build command - if (Get-Command bf -errorAction SilentlyContinue) { - # create generated folder if not - if (!(Test-Path generated)) { - $null = New-Item -ItemType Directory -Force -Path generated - } - bf luis:build --luConfig $(Join-Path $remoteBotPath luconfig.json) --botName $name --authoringKey $luisAuthoringKey --dialog crosstrained --out ./generated --suffix $environment -f --region $luisAuthoringRegion + # create generated folder if not + if (!(Test-Path generated)) { + $null = New-Item -ItemType Directory -Force -Path generated } + + # ensure bot cli is installed + if (Get-Command bf -errorAction SilentlyContinue) {} else { - Write-Host "bf luis:build does not exist, use the following command to install:" - Write-Host "npm install -g @microsoft/botframework-cli" - Break + Write-Host "bf luis:build does not exist. Start installation..." + npm i -g @microsoft/botframework-cli + Write-Host "successfully" } + # Execute bf luis:build command + bf luis:build --luConfig $(Join-Path $remoteBotPath luconfig.json) --botName $name --authoringKey $luisAuthoringKey --dialog crosstrained --out ./generated --suffix $environment -f --region $luisAuthoringRegion + if ($?) { Write-Host "lubuild succeeded" } @@ -142,7 +144,6 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { Set-Location -Path $projFolder - # clear the settings; we don't want to unintentionally copy secrets to the remote web app $settings = New-Object PSObject $luisConfigFiles = Get-ChildItem -Path $publishFolder -Include "luis.settings*" -Recurse -Force