I am attempting to edit my WebApp's Web.config using the Kudu WebApp API and an Azure Automation Runbook.
Logging into the WebApp via the Kudu interface and executing the code in the PowerShell debugger removes the XML node as expected:
$username = "`$myusername"
$password = "123456"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$ProgressPreference="SilentlyContinue"
$apiUrl = "https://mysite.scm.azurewebsites.net/api/command"
$pscommand = $webConfigPath = "D:\home\site\wwwroot\Web.config"
[xml] $webConfigXML = Get-Content $webConfigPath
#remove the following handler in the <httpHanderls> section in the web.config
$targetName = "Sitecore.FeedRequestHandler"
$nodePath = "configuration/system.webServer/handlers/add[#name='{0}']" -f $targetName
$node = $webConfigXML.SelectSingleNode($nodePath)
if($node -ne $null)
{
$webConfigXML.configuration.'system.webServer'.handlers.RemoveChild($node)
}
$webConfigXML.Save($webConfigPath)
$commandBody = #{
command = "powershell -command `"$pscommand`""
}
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method POST -ContentType "application/json" -Body (ConvertTo-Json $commandBody)
However, if I try to run this via PowerShell ISE or an Azure Automation Runbook, it produces the following errors:
Program 'Web.config' failed to run: Access is deniedAt line:1 char:1..
Get-Content : Cannot find path 'D:\home\site\wwwroot\Web.config'
because it does not exist.
Any ideas on how I can edit the XML of the Web.config via an Azure Automation Runbook?
Looking at your code, I think it is confusing two model:
Some of it is written to call Kudu from the outside via http, when you call the /api/command API.
Some of it is written to run directly from within Kudu, when you try to access D:\home\site\wwwroot\Web.config on the local file system.
2 has no chance of working if you run anywhere other than in Kudu.
What you need to do instead is use the Kudu vfs API to read the file, modify it locally, and then save it back using the vfs API.
Related
I'm fairly new to Azure cli. I've got a case where I'd like to perform a search on each of my code repos in Azure devops and check for the version of a dependency in the pom.xml file.
The front end search via the web console provides something very close to what i need but i can't easily export the results. In the front end I enter in a search like so and it kind of gives me what i need.
Whats the best way to replicate this behaviour to produce a kind of report using the azure cli?
In az cli, there is az repos to manage Azure Repos. You could run az repos show to get the details of a Git repository, but there is no Code search commands.
Instead of az cli, you could use Code Search Results - Fetch Code Search Results REST api in your script to get results of the search text.
POST https://almsearch.dev.azure.com/{organization}/{project}/_apis/search/codesearchresults?api-version=6.0-preview.1
The script uses the api looks like:
Param(
[string]$organisation = "org",
[string]$project = "projectname",
[string]$keepForever = "true",
[string]$user = " ",
[string]$token = "PAT" )
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$postresults = "https://almsearch.dev.azure.com/$organisation/$project/_apis/search/codesearchresults?api-version=6.0-preview.1"
$body = '{
}'
$result = Invoke-RestMethod -Uri $postresults -Method Post -Body $body -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
I have a single file that I need to deploy to an existing App Service (that already contains an application, deployed previously through another pipeline).
I would like to avoid using the FTP task for that.
Is there a way to deploy a single file to a specific folder in an App Service via a DevOps Pipeline?
Actually FTP seems to be the easiest way to deploy single file. However if you don't want to use it you can use KUDU API. What you need is to wrap it in powershell script:
$username = '$(username)'
$password = '$(password)'
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiUrl = "https://$(siteName).scm.azurewebsites.net/api/vfs/site/your-file"
$filePath = "$(System.DefaultWorkingDirectory)/your-file"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", ("Basic {0}" -f $base64AuthInfo))
$headers.Add("If-Match", "*")
Invoke-RestMethod -Uri $apiUrl -Headers $headers -Method PUT -InFile $filePath -ContentType "multipart/form-data"
Here you find info how to get credentials.
But if this is one time task you may use drag & drop from KUDU panel as it is mentioned here
I would like to download the test plan artifacts (files) from azure devops repository via command line or power shell script. I used this (Download an application from Azure Devops via command line) as a reference but I cannot use GIT / repository.
Is there any way I can download the test artifacts (files) for each test case/ test suite via command line or powershell?
I tested this Rest Api. The download parameter didn't worked as expected.
So there is an another workaround. You can specify the Output file path in the Invoke-RestMethod stage.
$result = Invoke-RestMethod -Uri $uri -OutFile D:\1212\test1.txt -Method Get -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
In my Azure DevOps Pipeline I would like to copy a folder e.g. Media from 1 environment/app service, say TEST to another environment/app service say Live. The Media folder in TEST may get updated AFTER the Ci/cd build has been deployed to the TEST environment - just to exclude answers that might suggest putting it in Git and including it as a Build artifact.
EDIT - Clarification on using the accepted answer.
My repo contains the given powershell script in the accepted answer as :
azure/Copy-Media-Test-To-Live.ps1
I then add the azure folder as an artifact in the build pipeline i.e.
Edit azure-pipelines.yml and Add:
- task: PublishPipelineArtifact#1
inputs:
path: $(System.DefaultWorkingDirectory)/azure/
artifact: azure
In the release pipeline - reference the script to perform the copy:
steps:
- task: AzurePowerShell#4
displayName: 'Azure PowerShell script: FilePath'
inputs:
azureSubscription: 'Your subscription '
ScriptPath: '$(System.DefaultWorkingDirectory)/_your-artifact-path/azure/Copy-Media-Test-To-Live.ps1'
azurePowerShellVersion: LatestVersion
Any application running in an App Service Environment can be managed via Kudu. Kudu has an API for downloading a ZIP compressed archive of any folder currently deployed to an application. It can be accessed with a GET request to:
https://{{YOUR-APP-NAME}}.scm.azurewebsites.net/api/zip/site/{{FOLDER}}
You can use the Invoke-WebRequest cmdlet in PowerShell to pull this content to local storage.
You do need to authenticate to use the Kudu API, which is easy in a browser, but when automating is a little more involved. Please see the following article which details how to retrieve and present a Basic Authorization header, as well as demonstrating how to use the command API to extract a ZIP file using the Invoke-RestMethod cmdlet. Your service principal will need at least contributor access to your applications to get deployment credentials to use in the API calls.
https://blogs.msdn.microsoft.com/waws/2018/06/26/powershell-script-to-execute-commands-in-scm-website-on-all-instances/
EDIT (Include worked example script):
If you have multiple subscriptions and the context has not been set properly in the deployment runtime environment, you may need to use Set-AzContext -Subscription "<SubsciptionName>" to set the context for getting the WebApp
$srcResGroupName = "Test"
$srcWebAppName = "tstest12"
$srcDirectory = "/site/wwwroot/myFolder/"
$dstResGroupName = "Test"
$dstWebAppName = "tstest123"
$dstDirectory = "/site/wwwroot/myFolder/"
# 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"
# Download the ZIP file to ./tmp.zip
Invoke-RestMethod -Uri "$apiBaseUrl/zip$($srcDirectory)" `
-Headers #{UserAgent="powershell/1.0"; `
Authorization=("Basic {0}" -f $base64AuthInfo)} `
-Method GET `
-OutFile ./tmp.zip
# Get publishing profile for DESTINATION application
$dstWebApp = Get-AzWebApp -Name $dstWebAppName -ResourceGroupName $dstResGroupName
[xml]$publishingProfile = Get-AzWebAppPublishingProfile -WebApp $dstWebApp
# 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://$($dstWebApp.Name).scm.azurewebsites.net/api"
# Upload and extract the ZIP file
Invoke-RestMethod -Uri "$apiBaseUrl/zip$($dstDirectory)" `
-Headers #{UserAgent="powershell/1.0"; `
Authorization=("Basic {0}" -f $base64AuthInfo)} `
-Method PUT `
-InFile ./tmp.zip `
-ContentType "multipart/form-data"
I'd ideally like to declare in my applications code or .deployment file for particular Site Extensions to be installed or updated when my code is pushed to the scm Kudu site. Is this possible?
Specifically I'd like the Microsoft.ApplicationInsights.AzureWebSites site extension to be automatically installed as part of all the sites I deploy without having to manually browse to the scm site and install it from the gallery.
You cannot install site extensions as part of Kudu git deployment, but you can do it as part of an ARM template. You can find a complete sample here.
This is basically the same as for any other site configurations. e.g. setting App Settings, app service tier, turning on logging, WebSockets, ... All those things sit outside of Kudu deployments, but can be achieved using an ARM template which has all your desired site state.
Another would be to make a WebJob that can create the SiteExtension folder and then copy your SiteExtension files in to that folder. As part of your deployment you would just include the webjob.
We do something similar with how Stackify APM is installed from a site extension in to Azure Web Apps. Site extension creates a WebJob and the WebJob actually updates the site extension applicationHost transform based on some conditional items.
It can be done using powershell, but it's a bit hacky (example using staging slot named staging):
Write-Host "Setting appsettings for Stackify on $AzureWebSite"
$hash = #{}
$hash['Stackify.ApiKey'] = "$licenceKey"
$hash['Stackify.Environment'] = "$environment"
$hash['Stackify.AppName'] = "$BaseWebAppName"
if ($loadCertificates -eq 'True')
{
$hash['WEBSITE_LOAD_CERTIFICATES'] = "*"
}
Set-AzureWebsite -Name $AzureWebSite -Slot staging -AppSettings $hash
### Install Extension for Azure App###
Write-Host "Installing Stackify on $AzureWebSite"
$Kudu = "https://" + $AzureWebSite + "-staging.scm.azurewebsites.net/api/extensionfeed" # Here you can get a list for all Extensions available.
$InstallNRURI = "https://" + $AzureWebSite + "-staging.scm.azurewebsites.net/api/siteextensions" # Install API EndPoint
$slot = Get-AzureWebsite $AzureWebSite -Slot staging
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $slot.PublishingUsername,$slot.PublishingPassword)))
$invoke = Invoke-RestMethod -Uri $Kudu -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method get ###-InFile $filePath -ContentType "multipart/form-data"
$id = ($invoke | ? {$_.id -match "stackify*"}).id ### Searching for Stackify ID Extension
try {
$InstallStackify = Invoke-RestMethod -Uri "$InstallNRURI/$id" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Put
$Status = ($InstallStackify.provisioningState).ToString() + "|" + ($InstallStackify.installed_date_time).ToString() ### Status
Write-Output "Stackify Installation Status : $Status"
}
catch{$_}
Restart-AzureWebsite $AzureWebSite -Slot staging