I want to deploy or copy the artifacts to a subfolder inside the wwwroot of azure app service - azure-devops

I'm using azure AzureWebApp#1 to publish the contents of my front end app, however it has to be deployed to a folder inside the wwwroot of the app service. I tried using the customDeployFolder but it is not working. Is there a way I can achieve this using yaml? Thanks

Thanks # levi-lu-msft , Your answer helped lot.
You can use the KUDU API to deploy the azure app service outside the wwwroot with the artifacts. You need to add an azure PowerShell task in your release pipeline and run kudu api. Below scripts is for example.
1, scripts to create a directory CustomDomain
$WebApp = Get-AzWebApp -Name '<appname>' -ResourceGroupName '<resourcegroupname>'
[xml]$publishingProfile = Get-AzWebAppPublishingProfile -WebApp $WebApp
# Create Base64 authorization header
$username = $publishingProfile.publishData.publishProfile[0].userName
$password = $publishingProfile.publishData.publishProfile[0].userPWD
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$bodyToPOST = #{
command = "md CustomDomain"
dir = "D:\home\site"
}
# Splat all parameters together in $param
$param = #{
# command REST API url
Uri = "https://<appname>.scm.azurewebsites.net/api/command"
Headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
UserAgent = "powershell/1.0"
Method = "POST"
Body = (ConvertTo-Json $bodyToPOST)
ContentType = "application/json"
}
# Invoke REST call
Invoke-RestMethod #param
Above scripts will first get the username and password from your app's publishprofile which will be used later as anthentication in calling kudu api. And the api will run your self-defined command to make directory CustomDomain in "d:\home\site"
2, Deploy your app using kudu api.
When the CustomDomain directory is created, you can invoke kudu api to deploy your app to CustomDomain directory. Please refer to below example.
$param = #{
# zipdeploy api url
Uri = "https://<appname>.scm.azurewebsites.net/api/zip/site/CustomDomain"
Headers = #{Authorization=("Basic {0}" -f $base64AuthInfo)}
UserAgent = "powershell/1.0"
Method = "PUT"
# Deployment Artifact Path
InFile = "$(System.DefaultWorkingDirectory)\<artifacts_alias>\drop\<artifacts_name>.zip"
ContentType = "multipart/form-data"
}
# Invoke REST call
Invoke-RestMethod #param
The value InFile should point to location of the artifact file which is downloaded by your release pipeline. Usually it is located in "$(System.DefaultWorkingDirectory)\<artifacts_alias>\drop\<artifacts_name>.zip"
Refer here for more info

Related

Azure Devops - Azure powershell task find release/deployment directory

I'm running Azure devops where i have a pipeline and a release (running on a self hosted agent), and the release is set to send up to an azure app service. The deployment works fine, the only issue is that i'd also like to be able to (based on some of my release variables) edit the web.config of the site AFTER it has already been deployed to the azure website.
I'm using the Azure Powershell task ( https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-powershell?view=azure-devops ) ,and i can't find anywhere in the release variables ( https://learn.microsoft.com/en-us/azure/devops/pipelines/release/variables?view=azure-devops&tabs=batch ) that shows the directory of where the site exists. Looking through Kudu, it's pretty basic, like d:\home\site\wwwroot\ , but using that doesn't work at all.
Is this post config that i'm looking for not really possible, or should i be approaching it a different way?
I think you can use powershell task to call Kudu api to get the deployed web.config and edit it using Magic Chunks task or File Transform task in your release pipeline. Then using Kudu api to upload the changed web.config to azure website again.
1, Below script shows how to get web.config from azure website.
$srcResGroupName = "Test"
$srcWebAppName = "tstest12"
$outwebconfig="$(System.DefaultWorkingDirectory)\tempfolder\web.config"
# Get publishing profile for SOURCE application
$srcWebApp = Get-AzWebApp -Name $srcWebAppName -ResourceGroupName $srcResGroupName
[xml]$publishingProfile = Get-AzWebAppPublishingProfile -WebApp $srcWebApp
# Create Base64 authorization header
$username = $publishingProfile.publishData.publishProfile[0].userName
$password = $publishingProfile.publishData.publishProfile[0].userPWD
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiBaseUrl = "https://$($srcWebApp.Name).scm.azurewebsites.net/api/vfs/site/wwwroot/web.config"
# Download the web.config file to $outwebconfig
Invoke-RestMethod -Uri "$apiBaseUrl" `
-Headers #{UserAgent="powershell/1.0"; `
Authorization=("Basic {0}" -f $base64AuthInfo)} `
-Method GET `
-OutFile $outwebconfig
Above script will download the web.config from the azure website and save it to $(System.DefaultWorkingDirectory)\tempfolder\web.config, where you can edit it with transform task later.
Above scripts get the username and password with scripts, you can also get them in the publish profile by going to the Overview blade on your App Service, clicking ...More at the top of the blade, and then clicking Get publish profile
2, Then you can add a config transform task to change your web.config according.
3, Last add a powershell task to upload the changed web.config to azure website
$srcResGroupName = "Test"
$srcWebAppName = "tstest12"
$webconfig="$(System.DefaultWorkingDirectory)\tempfolder\web.config"
# Get publishing profile for SOURCE application
$srcWebApp = Get-AzWebApp -Name $srcWebAppName -ResourceGroupName $srcResGroupName
[xml]$publishingProfile = Get-AzWebAppPublishingProfile -WebApp $srcWebApp
# Create Base64 authorization header
$username = $publishingProfile.publishData.publishProfile[0].userName
$password = $publishingProfile.publishData.publishProfile[0].userPWD
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiUrl = "https://$($srcWebApp.Name).scm.azurewebsites.net/api/vfs/site/wwwroot/web.config";
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method PUT -InFile $webconfig -ContentType "application/xml";
For more usage of Kudu api you can check here.

How to add a new dynamically discoverable capability to an agent?

Updates
I am going to propose a Capabilities Provider here as an update to my post.
If you need more details please let me know.
We currently have a bunch of shipped Capabilities Providers in the agent source code:
https://github.com/microsoft/azure-pipelines-agent/tree/master/src/Microsoft.VisualStudio.Services.Agent/Capabilities
Agent
Environment
Nix
PowerShell
What is being proposed is one additional Provider named ExecutableCapabilitiesProvider.
This new ExecutableCapabilitiesProvider will probably have a config file which can be edited on the agent machine.
The format of this file could probably be:
#name,executable
pip,pip3 freeze
xyz,/usr/bin/xyz-runner
abc,sh -C "ls -l /blah/blah"
As the maintainer of the self-hosted pool, I would configure this file with entries suiting me and have the agent run it as it starts. This way I am not hard-coding any values for my capabilities but rather those be determined at the start up.
And I would go one step further and add a new API call to add capabilities which is more flexible than the current one asking for name/values. An example, would be to change the parameters to Name, Provider, Params:
efg, NixProvider, /path/to/file/efg
klm, ExecutableCapabilitiesProvider, /usr/bin/klm -a -b -c
Original Post
I'd like to make my agents report on new capabilities which are not static but rather result of a command or something similar? How can I do that?
Our agents run on linux boxes.
To be specific, I'd like to have a new capability called pip-packages and the value for that is the result of the command pip freeze executed on the shell.
If you mean to add User-defined capabilities, then you can write a script to call the REST API to update the agent capabilities.
PUT https://dev.azure.com/{organization}/_apis/distributedtask/pools/{poolid}/agents/{agentid}/usercapabilities?api-version=5.0
Request body:
{"pip-packages": "xxxx"}
For example, you can set a variable and run command pip freeze and export the response as the value of that variable, then update the agent capability by calling the REST API:
Below PowerShell sample for your reference :
Param(
[string]$collectionurl = "https://dev.azure.com/{organization}",
[string]$poolid = "14",
[string]$agentid = "16",
[string]$user = "user",
[string]$token = "PAT/Password"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
# Run pip freeze command and get response as the value of the variable $pipfreeze (Just for your reference here, you need to extract the value with running the commands)
$pipfreeze = "response of pip freeze"
# Create json body with that value
$baseUri = "$collectionurl/_apis/distributedtask/pools/$poolid/agents/$agentid/usercapabilities?api-version=5.0"
function CreateJsonBody
{
$value = #"
{"pip-packages":"$pipfreeze"}
"#
return $value
}
$json = CreateJsonBody
# Update the Agent user capability
$agentcapability = Invoke-RestMethod -Uri $baseUri -Method Put -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
write-host "=========================================================="
Write-host "userCapabilities :" $agentcapability.userCapabilities.'pip-packages'
write-host "=========================================================="

How to create a new build or release using the API and YAML

I'm just looking for direction here as, possibly, the api already does this and I'm misunderstanding / can't find the right resource.
What I would like to do is to be able to call the azure-devops api to create a new build definition for me when I supply it with all the necessary yaml files for each stage.
I expected a create endpoint which would take in a few basic pieces of information to create the build / release definition then a collection of yaml files to create the tasks.
I've found Create your first pipeline and Api 5.0 BuildDefinition/Create however neither of these mention posting a yaml definition to the api. I was expecting far less items in the request body considering the yaml definitions contain most of the information required.
Does the api support this? Will it ever support this?
There is no docs for Rest Api with yaml, but if you try to get an existing yaml definition you`ll meet the next example:
So if you want to edit the process you have to edit existing yaml file. If you want create/clone an existing build definition you may try to create/clone yaml file and post a request (Definitions - Create) with the process member:
yamlFilename = path to yaml file in the repository
type = 2
This powershell example to clone a build definition with yaml:
$pat = '{personal access token}'
$base64AuthInfo = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$pat"))
$uri = 'https://dev.azure.com/{organization}/{team_project}/_apis/build/definitions/{buil_id}?api-version=5.0'
$result = Invoke-RestMethod -Method Get -Uri $uri -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -ErrorAction Stop
$body = $result | ConvertTo-Json -Depth 7
$existingyaml = '"yamlFilename": "{path to yaml for existing buildef}"'
$newyaml = '"yamlFilename": "{path to new yaml}"'
$buildname = '"name": "{existing build name}"'
$newbuildname = '"name": "{new build name}"'
$body = $body.Replace($existingyaml, $newyaml)
$body = $body.Replace($buildname, $newbuildname)
$Uri = "https://dev.azure.com/{organization}/{team_project}/_apis/build/definitions?api-version=5.0"
$newBuildDef = Invoke-RestMethod -Uri $Uri -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Post -Body $body -ContentType "application/json" -ErrorAction Stop
Yes, you are right, you could do a get on a build using the api, and change the variables, it should work.
If you only need to modify variables, you could use variable group to store values, then you can get the variable group and modify the variable values using the Variablegroups api.

Download secure file with PowerShell

I'm trying to create a new release task for VSTS which needs to download a secure file from the library. However, when I run the following PowerShell script no secure files are displayed but there are two in there. Could this be not having enough rights? What should be changed.
Another question: when I'm able to list the secure files I want to download a specific one. I haven't found any examples on how to do that. Does anyone know of an example?
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/distributedtask/securefiles"
Write-Host "URL: $url"
$secureFiles = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
Write-Host "SecureFiles: $secureFiles"
I was able to download Secure Files using a REST API, the task's Access Token, and an Accept header for application/octet-stream. I enabled "Allow scripts to access the OAuth token". Here my task.json is using a secureFile named "SecureFile."
$secFileId = Get-VstsInput -Name SecureFile -Require
$secTicket = Get-VstsSecureFileTicket -Id $secFileId
$secName = Get-VstsSecureFileName -Id $secFileId
$tempDirectory = Get-VstsTaskVariable -Name "Agent.TempDirectory" -Require
$collectionUrl = Get-VstsTaskVariable -Name "System.TeamFoundationCollectionUri" -Require
$project = Get-VstsTaskVariable -Name "System.TeamProject" -Require
$filePath = Join-Path $tempDirectory $secName
$token= Get-VstsTaskVariable -Name "System.AccessToken" -Require
$user = Get-VstsTaskVariable -Name "Release.RequestedForId" -Require
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User, $token)))
$headers = #{
Authorization=("Basic {0}" -f $base64AuthInfo)
Accept="application/octet-stream"
}
Invoke-RestMethod -Uri "$($collectionUrl)$project/_apis/distributedtask/securefiles/$($secFileId)?ticket=$($secTicket)&download=true&api-version=5.0-preview.1" -Headers $headers -OutFile $filePath
I am using "$(Build.QueuedById)" to get the user id in build tasks, but honestly I don't think it matters what string you use there.
If you don't have the Accept header, you'll get JSON metadata back for the file you're attempting to download.
Unfortunately I cobbled this together from other SO posts and the github issues pages; I can't find anywhere official that documents the URL I'm using there.
There has no such REST API to download secure file, but you can use Download secure file task for assistants.
And since the secure file only exist in temporary location during build, you should download the secure file by Download secure file task firstly, and copy the secure file to another directory secondly:
1. Download secure file
You can add a Download secure file task (for VSTS) and specify the filename to download.
Note: since the task is not available for TFS, you can install the similar task like Download Secure File extension for your TFS account.
2. Copy secure file to another directory
Such as copy the secure file to $(Build.ArtifactStagingDirectory), you can use PowerShell script:
Copy-Item -Path $(Agent.WorkFolder)\_temp\filename -Destination $(Build.ArtifactStagingDirectory)
Or use Copy Files task to copy the secure file to $(Build.ArtifactStagingDirectory).
BTW:
Since you are using Download Secure File task (developed by Matt Labrum) which can only select secure file from DropDownList (variables can not be used). But there has an issue Enable to use a variable to specify the secure file to download which suggests this feature, you can follow up.
And for the REST API to download secure file, it's not available for now. But there has an user voice Access "Secure files" from .NET client library, and you can vote and follow up.

TFS: Best way to trigger build on server restart, or on Windows Updates installation

In short, the requirement is to verify that our latest released software can be built and then installed after the latest Windows updates and/or other patches were applied. So the build server VM(s) will be configured just for this purpose and the build only needs to run after an update.
Since such updates usually are followed with a restart, I am thinking of a server restart event triggering a build and deployment. Does such option exist in TFS 2017?
If there is no way to do it through TFS then, I guess, a PowerShell script that runs on startup should work?
No such a build-in function to achieve that. However create a PowerShell script that runs on startup should work. Just as Jessehouwing said, you can create the script with the REST API to trigger builds.
Create a script to trigger the specific build definition. (Reference below sample)
Run the script on startup:
How to run a batch file each time the computer boots
How to schedule a Batch File to run automatically in Windows
10/8/7
Param(
[string]$collectionurl = "http://server:8080/tfs/DefaultCollection",
[string]$projectName = "ProjectName",
[string]$keepForever = "true",
[string]$BuildDefinitionId = "34",
[string]$user = "username",
[string]$token = "password"
)
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
function CreateJsonBody
{
$value = #"
{
"definition": {
"id": $BuildDefinitionId
},
"parameters": "{\"system.debug\":\"true\",\"BuildConfiguration\":\"debug\",\"BuildPlatform\":\"x64\"}"
}
"#
return $value
}
$json = CreateJsonBody
$uri = "$($collectionurl)/$($projectName)/_apis/build/builds?api-version=2.0"
$result = Invoke-RestMethod -Uri $uri -Method Post -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
There is no existing trigger that handles this, but there is a simple REST API to query and trigger builds.
It would be easy to create an on startup job in the task scheduler, use the REST API to query a list of Build Definitions based on a certain name or tag and then queue it.
List build definitions
Queue a build