Azure Websites Kudu REST API - Authentication - rest

I'm trying to use PowerShell to put an updated content file onto an Azure Website via the REST API. However, when supplying my credentials into Invoke-RestMethod -Credentials I am returned the HTML of the standard Azure login page.
How can I authenticate with Kudu from PowerShell? Thanks.

You can first get the website via Powershell and then use the publish credentials from the website to call the Kudu REST API. The example below will get the Kudu version.
$website = Get-AzureWebsite -Name "WebsiteName"
$username = $website.PublishingUsername
$password = $website.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiBaseUrl = "https://$($website.Name).scm.azurewebsites.net/api"
$kuduVersion = Invoke-RestMethod -Uri "$apiBaseUrl/environment" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method GET

In the new ARM world and with the latest PowerShell, you'll need to make some adjustments to #Seth's answer.
Specifically, the way you obtain the publishing creds is different, which is the first 3 lines. The rest I shamelessly copied from #Seth to complete the snippet.
Make sure to replace YourResourceGroup/YourWebApp as appropriate:
$creds = Invoke-AzureRmResourceAction -ResourceGroupName YourResourceGroup -ResourceType Microsoft.Web/sites/config -ResourceName YourWebApp/publishingcredentials -Action list -ApiVersion 2015-08-01 -Force
$username = $creds.Properties.PublishingUserName
$password = $creds.Properties.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiBaseUrl = "https://$($website.Name).scm.azurewebsites.net/api"
$kuduVersion = Invoke-RestMethod -Uri "$apiBaseUrl/environment" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method GET

Related

Authenticate to Azure DevOps without user's DevOps PAT

Currently we use an approach to reach DevOps and trigger "release pipelines" from a specific VM1 by utilizing user's DevOps PAT. We run PowerShell commands below at VM1:
$userPatToken = "xxxdfdgklfdgofkglfg4565gfhgfhgfh4gf54h54545fhfghfdffg"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "", $userPatToken)))
$url = "https://vsrm.dev.azure.com/MyOrg/MyProject/_apis/release/releases?definitionId=7&$top=100&api-version=6.0"
Invoke-RestMethod -Method Get -Uri $url -ContentType "application/json" -Headers #{Authorization = ("Basic {0}" -f $base64AuthInfo) }
The user is AAD one, is there a way to use it's let say AAD credentials and authenticate to DevOps and do the same?
Or may there is a way to use VMs system managed (or any user managed) identity to authenticate into DevOps and trigger release pipelines? We do not want to be dependent of the user's PAT.
It should be written in PowerShell.
If you don't want to use the PAT, you can install Az powershell module, login with a user account which has the permission in your devops org via Connect-AzAccount.
Then, you can get the token via below command. Please note don't change the 499b84ac-1321-427f-aa17-267ca6975798 in the script, it is the well-known resource id of the DevOps REST API.
$token = (Get-AzAccessToken -ResourceUrl "499b84ac-1321-427f-aa17-267ca6975798").Token
Then, you can pass the token to your powershell script. You can find more details/sample script here.
Edit:
Add username&Password automation script sample:
Install-Module -Name Az -Confirm:$False -Force -AllowClobber
Import-Module Az
$username = "useremail"
$password = "password"
$SecurePassword = ConvertTo-SecureString "$password" -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential($username, $SecurePassword)
Connect-AzAccount -Credential $credentials -TenantId yourTenantID
$token = (Get-AzAccessToken -ResourceUrl "499b84ac-1321-427f-aa17-267ca6975798").Token
$URL = 'https://dev.azure.com/{orgname}/{Projectname}/_apis/pipelines/{pipelineID}/runs?api-version=6.0-preview.1'
$header = #{
'Authorization' = 'Bearer ' + $token
'Content-Type' = 'application/json'
}
$body = #"
{
"resources": {
"repositories": {
"self": {
"refName": "refs/heads/master"
}
}
}
}
"#
Invoke-RestMethod -Method Post -Uri $URL -Headers $header -Body $body

Unable to deploy .zip file using Kudu api to the Azure App Service

I'm using PowerShell inline task in the release pipeline to deploy the respective zip file to the Azure App Service, but im unable to achieve it due to the below error. Can you please let me know if there is any thing that im missing here.
I'm getting below error
Invoke-RestMethod : Path 'D:\a\r1\a_CI-VI-Maven/DeployPackages/marnet.zip' resolves to a directory. Specify a path including a file name, and then retry the command.
Below is the script that im using:
$username = "username"
$password = "pwd"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$userAgent = "powershell/1.0"
$apiUrl = "https://{appservice}.scm.azurewebsites.net/api/zip/site/wwwroot/webapps/"
$filePath = "$(System.DefaultWorkingDirectory)_CI-VI-Maven/DeployPackages/marnet.zip"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method POST -InFile $filePath -ContentType "multipart/form-data"
Check your folder structure, it seems that you have a folder named marnet.zip!
Your issue occurred since the $filePath = "$(System.DefaultWorkingDirectory)_CI-VI-Maven/DeployPackages/marnet.zip" is a path to folder marnet.zip instead of a real marnet.zip file.
My reproducible steps:
1.Everything works well when my s.zip file locates directly under build artifacts folder.
2.Change something in Build pipeline to create s.zip folder, and move the s.zip file to this folder.
3.Then, same issue occurs:
Looking here
$username = "`$website"
$password = "pwd"
# Note that the $username here should look like `SomeUserName`, and **not** `SomeSite\SomeUserName`
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$userAgent = "powershell/1.0"
# Example 1: call the zip controller API (which uses PUT)
$apiUrl = "https://{sitename}.scm.azurewebsites.net/api/zip/site/wwwroot/"
$filePath = "C:\Temp\books.zip"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method PUT -InFile $filePath -ContentType "multipart/form-data"
# Example 2: call the zipdeploy API (which uses POST)
$apiUrl = "https://{sitename}.scm.azurewebsites.net/api/zipdeploy"
$filePath = "C:\Temp\books.zip"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method POST -InFile $filePath -ContentType "multipart/form-data"
So if you want to use zip controller API please change your verb to PUT insted of POST.

Rename or deploy additional files to Azure Web App

As part of my release pipeline in Azure DevOps, I wish to rename some .config files to .config.disabled, after my "Deploy Azure App Service" task has finished.
I have tried to add an additional "Deploy Azure App Service" task, but that seems to overwrite the previous one, leaving only the .config.disabled files in the wwwroot.
Is there any other way (other than FTP upload task), which can be used to rename/deploy a subset of files, in an Azure web app?
If you don't want to use FTP, you might have some success trying to use the Kudu service that backs the Azure Web App.
To view the Kudu service, navigate to https://<your-web-app-name>.scm.azurewebsites.net
After you've authenticated, you'll discover there's a way to browse files, view running processes and an interactive console that allows you to run basic DOS or PowerShell commands against the file system.
The interactive console is great, but to automate it you can issue commands against the file system using a REST API.
There are several examples of issuing commands to the server. In PowerShell:
# authenticate with Azure
Login-AzureRmAccount
$resoureGroupName = "your-web-app-name"
$websiteName = "your-resource-group-name"
$env = #{
command= 'Set COMPUTERNAME'
dir= 'site'
}
$json = $env | ConvertTo-Json
$env2 = #{
command= 'copy filename.config file.config.notused'
dir= 'site\wwwroot'
}
$json2 = $env2 | ConvertTo-Json
$env3 = #{
command= 'delete filename.config'
dir= 'site\wwwroot'
}
$json3 = $env3 | ConvertTo-Json
# setup auth header
$website = Get-AzureWebsite -Name $websiteName
$username = $website.PublishingUsername
$password = $website.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiBaseUrl = "https://$($website.Name).scm.azurewebsites.net/api"
[System.Uri]$Uri = $apiBaseUrl
# get all the vms in the web-app
$instances = Get-AzureRmResource -ResourceGroupName $resoureGroupName `
-ResourceType Microsoft.Web/sites/instances `
-ResourceName $websiteName `
-ApiVersion 2018-02-01
#loop through instances
foreach($instance in $instances)
{
$instanceName = $instance.Name
Write-Host "`tVM Instance ID `t`t: " $instanceName
#Now execute 'SET COMPUTER' cmd
$cookie= New-Object System.Net.Cookie
$cookie.Name = "ARRAffinity"
$cookie.Value = $instanceName
$Cookie.Domain = $uri.DnsSafeHost
$session=New-Object Microsoft.Powershell.Commands.WebRequestSession
$session.Cookies.add($cookie)
$response = Invoke-RestMethod -Uri "$apiBaseUrl/command" `
-Headers #{Authorization=("Basic {0}" `
-f $base64AuthInfo)} `
-Method Post -Body $json `
-ContentType 'application/json' `
-WebSession $session
Write-Host "`tVM Instance Name `t: " $response
# perform copy file
$response = Invoke-RestMethod -Uri "$apiBaseUrl/command" `
-Headers #{Authorization=("Basic {0}" `
-f $base64AuthInfo)} `
-Method Post -Body $json2 `
-ContentType 'application/json' `
-WebSession $session
Write-Host "`tCopy file result `t: " $response
# perform delete file
$response = Invoke-RestMethod -Uri "$apiBaseUrl/command" `
-Headers #{Authorization=("Basic {0}" `
-f $base64AuthInfo)} `
-Method Post -Body $json3 `
-ContentType 'application/json' `
-WebSession $session
Write-Host "`tCopy file result `t: " $response
}
The file system supports basic commands, like copy and delete so you could easily rename the file.

Save-AzrWebApp func downloads a wrong SourcePath

I'm using Save-AzrWebApp function to downloads files from an Azure Web App.
How to do it is described here: https://blog.ipswitch.com/how-to-copy-files-from-an-azure-app-service-with-powershell
My problem is: it doesn't matter which SourcePath I set, it always downloads me files from wwwroot folder.
Code example that I use:
$syncParams = #{
SourcePath = '\wwwroot\history'
TargetPath = $TargetPath
ComputerName = "https://$Name.scm.azurewebsites.net:443/msdeploy.axd?site=$Name"
Credential = $Credential
}
Sync-Website #syncParams
Get-Item -Path $TargetPath
Actually it doesn't matter what I put into SourcePath (even not existing path) it will download content of wwwroot.
How to use it in a proper way?
If you want to download file from web app, you could use this web app kudu api via powershell.
Try the command below, it works fine on my side.
$creds = Invoke-AzureRmResourceAction -ResourceGroupName joywebapp -ResourceType Microsoft.Web/sites/config -ResourceName joywebapp2/publishingcredentials -Action list -ApiVersion 2015-08-01 -Force
$username = $creds.Properties.PublishingUserName
$password = $creds.Properties.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$apiUrl = "https://joywebapp2.scm.azurewebsites.net/api/vfs/site/wwwroot/Content/Site.css"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo);"If-Match"="*"} -Method GET -ContentType "multipart/form-data" -OutFile "C:\Users\joyw\Desktop\test.css"
Test result:
Update:
If you want to download a folder, you can use the Zip api in the doc I mentioned, it allows downloading folder as a zip file.
Sample command:
$creds = Invoke-AzureRmResourceAction -ResourceGroupName joywebapp -ResourceType Microsoft.Web/sites/config -ResourceName joywebapp2/publishingcredentials -Action list -ApiVersion 2015-08-01 -Force
$username = $creds.Properties.PublishingUserName
$password = $creds.Properties.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$apiUrl = "https://joywebapp2.scm.azurewebsites.net/api/zip/site/wwwroot/Scripts/"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo);"If-Match"="*"} -Method GET -ContentType "multipart/form-data" -OutFile "C:\Users\joyw\Desktop\Scripts.zip"
Note:The zip doesn't include the top folder itself. Make sure you include
the trailing slash, e.g, I download the Scripts folder, we need to use Scripts/ in the apiUrl.

Azure Web App - upload package from powershell

I've used New-AzureRmWebApp to create WebApp from powershell. But I don't find any cmdlet to upload package to created Azure Web App :( Looking for AzureRm cmdlet in specific.
If you are co-admin of your subscription,you can use command Publish-AzureWebsiteProject -Name site1 -Package .\WebApplication1.zip to do that.We also can use kudu Rest API (Zip).More detail about how to get authorization please refer to another SO thread
kudu Rest Api sample code:
$website = Get-AzureWebsite -Name "WebAppSampleFtn2"
$username = $website.PublishingUsername
$password = $website.PublishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$userAgent = "powershell/1.0"
$apiBaseUrl = "https://$($website.Name).scm.azurewebsites.net/api/zip/site/wwwroot"
$filePath = "D:\package\PackageTmp.zip"
Invoke-RestMethod -Uri $apiBaseUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method PUT -InFile $filePath -ContentType "multipart/form-data"