How to create resources in Azure Portal using Devops CI/CD pipeline and ARM Template - azure-devops

I am working on creating qnamaker based Chatbot and for this I have prepared ARM template for creating and deploying resources.
PowerShell script is being used to create knowledge base once all the resources are created.
This process works just fine when I am executing PowerShell script from local PowerShell tool.
Now I want to do CI/CD for this deployment process so that deployment process can be automated
Creating resources and deploying to azure through Pipeline is quite possible through Azure ARM Template deployment task but I am not sure how to execute PowerShell script that is responsible for creating knowledge base once resources are created based on recently created qnamaker service
Any help greatly appreciated

You can refer to Create a CI/CD pipeline for .NET with the Azure DevOps Project for step by step lab to create the pipelines.
If you need help on ARM templates (if you are stuck somewhere), you can check out A quick guide on writing your Azure Resource Manager (ARM) Templates. and get started quickly.

Please clarify that your issue is creating and training the QnA Maker KB after the actual resources have been created by ARM? There are probably multiple ways to do this, but here is what I have done. First of all, I am using the Azure CLI Task version 2.* which simplifies some of the initial steps. You could probably use regular PowerShell for some of the subsequent steps. I have selected all as PowerShell script types and inline scripts. Each of these segments I do as a separate task but you could probably do all as one. I like being able to see which step fails if something happens, though.
First you need to get the key for your cognitive service
$QNAKEY= & az cognitiveservices account keys list -g "YOUR_RESOURCE_GROUP" --name "YOUR_QNA_RESOURCE_NAME" --query key1 -o tsv
Write-Output("##vso[task.setvariable variable=QNA_KEY;]$QNAKEY")
Next you need to create the KB. I have mine seeded with information via a json file in my project repository. You can do this from wherever, or just create a blank KB (I think). Note I'm checking here to see if the KB exists; KB name does not have to be unique, so if you don't do this you will end up creating a lot of duplicate KBs.
$header = #{
"Content-Type"="application/json"
"Ocp-Apim-Subscription-Key"="$(QNA_KEY)"
}
$res = Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/knowledgebases" -Method 'Get' -Headers $header
$kb = $res.knowledgebases | Where-Object name -eq "YOUR_KB_NAME"
Write-Host $res.knowledgebases
Write-Host $kb
if (!$kb) {
Write-Host "KB does not exist, so creating new KB"
$body = Get-Content '$(System.DefaultWorkingDirectory)/PATH_TO_MY_MODEL.json' | Out-String
Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/knowledgebases/create" -Method 'Post' -Body $body -Headers $header
}
Finally, you will likely want to publish your KB. With LUIS I think you need to train it first (via separate CLI task), but QnA Maker you should be able to publish directly. I do this as a dependent stage to ensure the KB is created before I try to publish.
$QNAKEY= & az cognitiveservices account keys list -g "YOUR_RESOURCE_GROUP" --name "YOUR_QNA_RESOURCE_NAME" --query key1 -o tsv
$header = #{
"Content-Type"="application/json"
"Ocp-Apim-Subscription-Key"="$QNAKEY"
}
$res = Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/knowledgebases" -Method 'Get' -Headers $header
$kb = $res.knowledgebases | Where-Object name -eq "YOUR_KB_NAME"
$qnaId = $kb.id
Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/qnamaker/v4.0/knowledgebases/$qnaId" -Method 'Post' -Headers $header
And that's it! At this point your QnA Maker knowledgebase should be created, published, and ready to go.

Related

Checking Functions exist on FunctionApp in Azure via DevOps Pipeline

On Azure DevOps I have a build/deploy YAML pipeline which builds my Function App and deploys this to a Linux Function App in Azure (hosted on an app-service plan).
However, recently I noticed an issue where the pipeline was showing the function app was successfully deployed, but when going into the FunctionApp in Azure and clicking on the Functions tab on the left, it shows "No results", but there should be 4 functions in there.
I want to have a step at the end of the deploy pipeline which checks that 4 functions exist in this FunctionApp, and to fail the pipeline run if this is not the case.
I know this will most likely be a task in the pipeline using Azure CLI or Powershell, but not sure how I would go around writing the script. Any help would be immensely appreciated.
Thanks all,
You could use an Azure CLI task and call the Rest API to list the functions withint the FunctionApp. I don't think the native Az CLI and Azure PowerShell functions expose the functions within the FunctionApp/
URI = "https://management.azure.com/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.Web/sites/<functionAppName>/functions?api-version=2015-08-01"
az rest -u $URI --method get | jq '.value[].name'
steps:
- powershell: |
#get token
$TENANTID="xxx"
$APPID="xxx"
$PASSWORD="xxx"
$result=Invoke-RestMethod -Uri https://login.microsoftonline.com/$TENANTID/oauth2/token?api-version=1.0 -Method Post -Body #{"grant_type" = "client_credentials"; "resource" = "https://management.core.windows.net/"; "client_id" = "$APPID"; "client_secret" = "$PASSWORD" }
$token=$result.access_token
##set Header
$Headers=#{
'authorization'="Bearer $token"
'host'="management.azure.com"
}
$functions = Invoke-RestMethod -Uri "https://management.azure.com/subscriptions/<subcription id>/resourceGroups/<resource group name>/providers/Microsoft.Web/sites/<function app name>/functions?api-version=2015-08-01" -Headers $Headers -ContentType "application/json" -Method GET
if($functions.value.Count -eq 4) {
# make pipeline to succeed
Write-Host 'Function deployment success.'
exit 0
}
else {
Write-Host 'Function deployment failed.'
exit 1
}
displayName: 'Check whether the function app deployment completed.'
If you have concerns, let me know.

Azure cli: clone pipeline

Looking at az pipelines documentation it seems it's not possible to clone a pipeline using cli.
I've looked at getting the yaml (az pipelines show -name=x > x_orig.yaml) and then trying to change json and create pipeline from modified yaml, but that feels like a lot of work that could break after next update.
Is there a way to clone a pipline without going the the Web UI?
Currently, there indeed is not available Azure CLI that can clone or export/import a pipeline to create a new pipeline.
I also searched and tried the Azure DevOps REST API for Pipelines, but did not find the available API.
Ideally, the Azure CLI "az pipelines create" can provide an input parameter that allows users specify an existing pipeline as a starting point for the new pipeline.
If your projects really need this feature, I recommend that you can directly report a feature request on the "Azure/azure-cli" repository to ask adding the parameter like as above mentioned. That will allow you directly interact with the appropriate engineering team, and make it more convenient for the engineering team to collect and categorize your suggestions.
As a workaround, we could clone the build definition via power shell script to call REST API.
Note: We need to change the original build definition name.
REST API
Get build definition:
GET https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}?api-version=6.0
Create build definition
POST https://dev.azure.com/{organization}/{project}/_apis/build/definitions?api-version=6.0
Power shell script
$connectionToken="{pat}"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$BuildDefinitionInfoURL = "https://dev.azure.com/{org name}/{project name}/_apis/build/definitions/386"
$BuildDefinitionInfo = Invoke-RestMethod -Uri $BuildDefinitionInfoURL -Headers #{authorization = "Basic $base64AuthInfo"} -Method Get
Write-Host $BuildDefinitionInfo.name
$BuildDefinitionInfo.name = $BuildDefinitionInfo.name +" clone"
Write-Host $BuildDefinitionInfo.name
$body = $BuildDefinitionInfo | ConvertTo-Json -Depth 99
$createBuildDefinitionURL = "https://dev.azure.com/{org name}/{project name}/_apis/build/definitions?api-version=6.0"
$response = Invoke-RestMethod -Uri $createBuildDefinitionURL -ContentType "application/json" -Body $body -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method POST
Write-Host $response.id
Result:

LUIS Azure DevOps pipeline, how to create any samples?

There is a requirement, to create Azure Devops pipeline which could support version, add file into application, train, test & publish application into Stage & production.
How it can be done through Azure Devops Pipeline. Any documentation or steps could be helpful.
Thanks
A.Prabhuram
I have done something similar through Azure CLI (PowerShell version 2.X) tasks, but it is not straight forward. I haven't done all of the steps as you mention above, but hopefully this will give you what you need to further build on it.
As a baseline, the functions you need are outlined in the LUIS Programmatic API. You will need the LUIS key and app id for most requests, which you can get via
$LUISKEY= & az cognitiveservices account keys list -g "resourceGroupName" --name "LUISauthoringKeyName" --query key1 -o tsv
$header = #{"Ocp-Apim-Subscription-Key"="$LUISKEY"}
$res = Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/?take=1" -Method 'Get' -Headers $header
$appid = $res.id
For brevity, I'm not repeating this code. But if you have separate tasks and/or agent jobs in your pipeline (as opposed to doing this as one script, which I do not recommend), you'll need to repeat these statements for each task. Do take note of the region and modify as needed for your authoring resource.
Obviously, to update your LUIS version, you need to have the model definition. I don't do this often since we are set up to use the same LUIS app in QA and PROD. So I just add a new version to the project repo if I need to run it through DevOps. Then I add the repo as an artifact for the release pipeline. But you should be able to use the Export Application Version API to get it programmatically, though I haven't tried personally. Here is what I did to add the new version:
$body = Get-Content '$(System.DefaultWorkingDirectory)/_AveryCreek_OEM_CSC_Bot/models/luis/AveryCreek OEM_CSC Team.json' | Out-String
Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/$appid/versions/import" -Method 'Post' -Body $body -Headers $header
Note that this version is not additive and will completely replace your previous version (though you can revert). In other words, if you have changes to the previous version that are NOT incorporated into the version you are importing, they will be lost. This is one of the main reasons we do not use separate LUIS apps per environment (sidebar - you can use separate predicition resources so you don't use production capacity while testing, but all of the endpoint utterances still go through to the one application).
Once the version is imported, you need to train and publish it. I personally don't have any testing built in, but I'm sure you could build some calls via the LUIS Prediction API and check for expected results. To train, you first need to grab the version, then call the training endpoint.
$res = Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/$appid/versions" -Method 'Get' -Headers $header
$version = $res[0].version
Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/$appid/versions/$version/train" -Method 'Post' -Headers $header
The next part is the most tricky. You can't publish the app until it is done training. To mitigate the risk of trying to publish before it is ready, I have created a separate agent job in my "Train and Publish" task to delay it.
This is typically enough delay, but I also have a check on training status and throw an error if it is not ready. Here is the snippet to get and check status and then publish.
$status = Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/$appid/versions/$version/train" -Method 'Get' -Headers $header
if ($status.details[0].status -ne "Success" -and $status.details[0].status -ne "UpToDate") { throw }
Invoke-RestMethod -Uri "https://westus.api.cognitive.microsoft.com/luis/authoring/v3.0-preview/apps/$appid/publish" -Method 'Post' -Body $body -Headers $header
And that should do it! As I mentioned, take care to make sure you define things like the LUIS Key and App ID in each task, as I have not repeated all of those values here. And you can add additional tasks to programmatically export the version (make sure you get the right key for your source app) and test the model as desired.

Store result of Azure CLI task into release variable

I have a release pipeline with multiple jobs.
On the first agent job there is an Azure CLI task that retrieves the keys of a storage account on azure.
The command it executes is :
az storage account keys list --account-name "$(diagnosticsStorageAccountName)" --resource-group "$(resourceGroup)"
What I want to do is store the result of this command and utilize it in a task that is running under a deployment group job.
I've already looked into these resource :
Set Output Variable in Azure CLI task on VSTS
How to modify Azure DevOps release definition variable from a release task?
I've tried the first one but I didn't get it working.
I didn't bothered with the second because it seems way to hacky.
Is there any way do achieve this that isn't hacky ?
The output values didn't get stored properly.
The output of az storage account keys list --account-name "$(diagnosticsStorageAccountName)" --resource-group "$(resourceGroup)"is spread over multiple lines when you use the following syntax:
echo "##vso[task.setvariable variable=testvar;]%myvar%"
So one of the problems was that only the first line of the JSON array was being stored into the variable.
I solved this problem in the following way:
keys=`az storage account keys list --account-name "$(diagnosticsStorageAccountName)" --resource-group "$(resourceGroup)"`
taskvariable="##vso[task.setvariable variable=_[tempVariable];]"
echo $taskvariable$keys
According to documentation echo "##vso[task.setvariable variable=testvar output=true;]%myvar% should make the variable available for the whole release. Unfortunately I had no luck with this.
I overcame this using a Powershell task under the same Agent Job (Ubuntu 16.0) :
$url = "$($env:SYSTEM_TEAMFOUNDATIONSERVERURI)$env:SYSTEM_TEAMPROJECTID/_apis/Release/definitions/$($env:RELEASE_DEFINITIONID)?api-version=5.1"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $(System.AccessToken)"
}
#Parsing JSON string from previous task
$keys = $(#"
$(_tempVariable)
"# | ConvertFrom-Json)
# Assignment of variable value
$pipeline.variables.[variableName].value = $keys[0].value
####****************** update the modified object **************************
$json = #($pipeline) | ConvertTo-Json -Depth 99
$updatedef = Invoke-RestMethod -Uri $url -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization = "Bearer $(System.AccessToken)"}
write-host "=========================================================="
Write-host "The value of Variable '[variableName]' is updated to" $updatedef.variables.[variableName].value
write-host "=========================================================="
Please note that in order to get this working there are a couple of things you need to do.
First you need to allow access to the OAuth token on the Agent Job.
On top of that you need to give the "Project Collection Build Service".
Click on security on the release and click on the "Project Collection Build Service" user.
Change the values for the "Edit release" and "Manage release" to allow and save the changes.

Automatically Export Build and Release definitions using Powershell or DEVOPS tasks

Have a requirement to automatically export specific Azure DEVOPS Build/Release definitions. I know the names of the definitions required. The process would run weekly to capture the information. I know the export can be done manually but want to automate process. Hoping Powershell script can be used.
Thanks
Joe
If you want to export the build/release definition automatically, you'd better use Powershell task with Rest API. But if this, it is not enough for just know the build definition name.
Refer to these docs: get build definition and get release definition. You can see that definitionid is necessary. In fact, this definitionid is very easy to get. Just click the relevant pipeline you want to export, the definitionid will display in URL:
To export the definition, you can use the follow script in powershell:
$headers = #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
$projectsUrl = "https://dev.azure.com/{org}/{project}/_apis/build/definitions/{build definitionid}?api-version=5.1"
$result = Invoke-RestMethod -Uri $projectsUrl -Method Get -Headers $headers
$filename=$result.name+".json"
$filePath="D:\"
$file=$filePath+$filename
$result | ConvertTo-Json | Out-File -FilePath $file
In this script, I specified the build name as the file name($filename=$result.name+".json"), and also, convert the result content as JSON to make the local file more readable:
Similarly, to get the release definition, just change the url as get release difinition:
$headers = #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
$projectsUrl = "https://vsrm.dev.azure.com/{org}/{project}/_apis/release/definitions/{definitionId}?api-version=5.1"
$result = Invoke-RestMethod -Uri $projectsUrl -Method Get -Headers $headers
$filename=$result.name+".json"
$filePath="D:\"
$file=$filePath+$filename
$result | ConvertTo-Json | Out-File -FilePath $file
Note: While use #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }, you'd enable Allow scripts to access the OAuth token to make the environment variable available during build pipeline.
In addition, as what you want is capture the information weekly, you can Schedule the pipeline which has these export task:
Now, these export pipeline will run and export the definition weekly.
You'll be looking at the az pipelines release and az pipelines build commands from Azure DevOps CLI
Commands Reference
Extension Reference