It can be a powerful tool to give your release process the ability to manipulate configuration files on the server that aren’t directly controlled by your source code.
The primary use cases i’ve used this for is to at deployment time re-wire connection strings on the fly or to have the stock Sitecore web.config be deployed stock and managed by the release process through remote transforms. Whatever your devops constraints are this technique could come in handy.
The technique is fairly simple:
Note: This requires the loading of the Microsoft.Web.XmlTransform.dll DLL, so make sure that’s available.
param( [string]$KuduPath, [string[]]$XDTs, [string]$TenantId, [string]$SubscriptionId, [string]$ResourceGroupName, [string]$WebAppServiceName, [string]$SlotName = "", [string]$ServicePrincipalID, [string]$ServicePrincipalKey, [hashtable]$Tokens ) function Get-AzureRmWebAppPublishingCredentials($resourceGroupName, $webAppName, $slotName = $null){ if ([string]::IsNullOrWhiteSpace($slotName)){ $resourceType = "Microsoft.Web/sites/config" $resourceName = "$webAppName/publishingcredentials" } else{ $resourceType = "Microsoft.Web/sites/slots/config" $resourceName = "$webAppName/$slotName/publishingcredentials" } $publishingCredentials = Invoke-AzureRmResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force return $publishingCredentials } function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName, $slotName = $null){ $publishingCredentials = Get-AzureRmWebAppPublishingCredentials $resourceGroupName $webAppName $slotName $ret = @{} $ret.header = ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword)))) $ret.url = $publishingCredentials.Properties.scmUri return $ret } function Get-FileFromWebApp($resourceGroupName, $webAppName, $slotName = "", $kuduPath){ $KuduAuth = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $webAppName $slotName $kuduApiAuthorisationToken = $KuduAuth.header $kuduApiUrl = $KuduAuth.url + "/api/vfs/site/wwwroot/$kuduPath" Write-Host " Downloading File from WebApp. Source: '$kuduApiUrl'." -ForegroundColor DarkGray $tmpPath = "$($env:TEMP)\$([guid]::NewGuid()).xml" $null = Invoke-RestMethod -Uri $kuduApiUrl ` -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} ` -Method GET ` -ContentType "multipart/form-data" ` -OutFile $tmpPath $ret = Get-Content $tmpPath | Out-String Remove-Item $tmpPath -Force return $ret } function Write-FileToWebApp($resourceGroupName, $webAppName, $slotName = "", $fileContent, $kuduPath){ $KuduAuth = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $webAppName $slotName $kuduApiAuthorisationToken = $KuduAuth.header $kuduApiUrl = $KuduAuth.url + "/api/vfs/site/wwwroot/$kuduPath" Write-Host " Writing File to WebApp. Destination: '$kuduApiUrl'." -ForegroundColor DarkGray Invoke-RestMethod -Uri $kuduApiUrl ` -Headers @{"Authorization"=$kuduApiAuthorisationToken;"If-Match"="*"} ` -Method Put ` -ContentType "multipart/form-data"` -Body $fileContent } function Update-XmlDocTransform($xml, $xdt, $tokens) { Add-Type -LiteralPath "$PSScriptRoot\Microsoft.Web.XmlTransform.dll" $xmldoc = New-Object Microsoft.Web.XmlTransform.XmlTransformableDocument; $xmldoc.PreserveWhitespace = $true $xmldoc.LoadXml($xml); $useTokens = $false if ($tokens -ne $null -and $tokens.Count -gt 0){ $useTokens = $true $sb = [System.Text.StringBuilder]::new((Get-Content -Path $xdt)) $tmpPath = "$($env:TEMP)\$([guid]::NewGuid()).xdt" $tokens.Keys | ForEach-Object{ $null = $sb.Replace($_, $tokens[$_]) } Set-Content -Path $tmpPath -Value $sb.ToString() $xdt = $tmpPath } $transf = New-Object Microsoft.Web.XmlTransform.XmlTransformation($xdt); if ($transf.Apply($xmldoc) -eq $false) { throw "Transformation failed." } if ($useTokens){ Remove-Item -Path $xdt -Force } return $xmldoc.OuterXml } $Credential = New-Object -TypeName PSCredential($ServicePrincipalID, (ConvertTo-SecureString -String $ServicePrincipalKey -AsPlainText -Force)) # Connect to Azure using SP $connectParameters = @{ Credential = $Credential TenantId = $TenantId SubscriptionId = $SubscriptionId } Write-Host 'Connecting to Azure.' $null = Add-AzureRmAccount @connectParameters -ServicePrincipal $contents = Get-FileFromWebApp ` -resourceGroupName $ResourceGroupName ` -webAppName $WebAppServiceName ` -slotName $SlotName ` -kuduPath $KuduPath $XDTs | Foreach-Object{ $contents = Update-XmlDocTransform -xml $contents -xdt "$PSScriptRoot\XDT\$_.xdt" -tokens $Tokens } Write-FileToWebApp ` -resourceGroupName $ResourceGroupName ` -webAppName $WebAppServiceName ` -slotName $SlotName ` -kuduPath $KuduPath ` -fileContent $contents
Additionally with some simple adaptations of this code you can use Kudu to perform whatever kind of file manipulations you want using a similar technique.