Forced Conversion from AzureRM to AZ powershell - azure

We have found that our AzureRM scripts have started to fail with Request to a Error downlevel service failed. This has forced us to change our scripts to start using the AZ powershell module, https://learn.microsoft.com/en-us/powershell/azure/new-azureps-module-az?view=azps-1.6.0. The conversion has worked really well except I haven't found the replacement for New-AzureWebsiteJob. Has anyone else run into this?

For New-AzureWebsiteJob cmdlet, there is no direct equivalent in the Az or ARM PowerShell Cmdlets.
You can follow this blog to achieve your purpose, and note that if you are using Az powershell module, please modify ARM Powershell to Az powershell respectively.
Sample code for Az powershell like below:
#Resource details :
$resourceGroupName = "<Resourcegroup name>";
$webAppName = "<WebApp name>";
$Apiversion = 2015-08-01
#Function to get Publishing credentials for the WebApp :
function Get-PublishingProfileCredentials($resourceGroupName, $webAppName){
$resourceType = "Microsoft.Web/sites/config"
$resourceName = "$webAppName/publishingcredentials"
$publishingCredentials = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType
$resourceType -ResourceName $resourceName -Action list -ApiVersion $Apiversion -Force
return $publishingCredentials
}
#Pulling authorization access token :
function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $webAppName){
$publishingCredentials = Get-PublishingProfileCredentials $resourceGroupName $webAppName
return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f
$publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}
$accessToken = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $webAppname
#Generating header to create and publish the Webjob :
$Header = #{
'Content-Disposition'='attachment; attachment; filename=Copy.zip'
'Authorization'=$accessToken
}
$apiUrl = "https://$webAppName.scm.azurewebsites.net/api/<Webjob-type>/<Webjob-name>"
$result = Invoke-RestMethod -Uri $apiUrl -Headers $Header -Method put -InFile "<Complete path of the file>\
<filename>.zip" -ContentType 'application/zip'

Related

No route registered for '/api/functions/admin/token'

I have 2 functions and have written a powershell script to get the JWT for the Azure Function app. In one of the functions, it's working and in the other, it's not. Below is a part of my script which I am using for generating the JWT:
$ResourceGroupName = <resource_group_name>
$functionAppName = <function_app_name>
$resourceType = "Microsoft.Web/sites/config"
$resourceName = "$functionAppName/publishingcredentials"
$publishingCredentials = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType $resourceType -ResourceName $resourceName -Action list -ApiVersion 2015-08-01 -Force
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword)))
$jwt = Invoke-RestMethod -Uri "https://$functionAppName.scm.azurewebsites.net/api/functions/admin/token" -Headers #{Authorization = ("Basic {0}" -f $base64AuthInfo) } -Method GET
The only difference that I could see between the 2 function apps is: the script works for a Java function of Version 2 and the function where the script doesn't work is a Python function of Version 3.
I also tried to generate a token from Azure official documentation and it failed with the same error No route registered for '/api/functions/admin/token'. I then created a new function app in Python (Version 3) in a different subscription and tried the same and it worked for the new function app, but not for the previous one.
Here are a few lines before the error message:
Invoke-RestMethod: /home/vsts/work/1/s/setKeyFunctionCode.ps1:72
Line |
72 | $jwt = Invoke-RestMethod -Uri "https://$functionAppName.scm.azure …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| No route registered for '/api/functions/admin/token'
Is there some configuration that I'm missing out?
Please help me figure out the issue. Thanks in advance.

In azure can we update the profile.ps1 file function app?

We have a function in azure portal. When we create function app, we can see the profile.ps1 in App Files section of a function App. Can we edit this profile.ps1 file using PowerShell or CLI commands. If Yes, please help me out.
Any help can be appriciated...!
In this case, your option is to use Kudu API via powershell to update the profile.ps1, in my sample, I store the new profile.ps1 with the path C:\Users\Administrator\Desktop\profile.ps1 in local, it works fine on my side.
Sample:
$appsvWebAppName = "<functionapp-name>"
$resourceGroupName = "<group-name>"
$resource = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType Microsoft.Web/sites/config -ResourceName "$appsvWebAppName/publishingcredentials" -Action list -ApiVersion 2018-02-01 -Force
$username = $resource.Properties.publishingUserName
$password = $resource.Properties.publishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$userAgent = "powershell/1.0"
$apiUrl = "https://$appsvWebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/profile.ps1"
$filePath = "C:\Users\Administrator\Desktop\profile.ps1"
$headers = #{
'Authorization' = 'Basic ' + $base64AuthInfo
'If-Match' = '*'
}
Invoke-RestMethod -Uri $apiUrl -Headers $headers -UserAgent $userAgent -Method PUT -InFile $filePath -ContentType "multipart/form-data"
Check in the portal:

Azure Web Job Zip deployment Error due to Size

I am deploying a web job through powershell script and can manage to get the publishing credentials and then add the access token in the authorization header. All is fine until it uploads the zip file when I receive file size error: The remote server returned an error: (413) Request Entity Too Large.
#Function to get Publishing credentials for the WebApp :
function Get-PublishingProfileCredentials($resourceGroupName, $AppServiceNameToDeployWebJobs) {
$resourceType = "Microsoft.Web/sites/config"
$resourceName = "$AppServiceNameToDeployWebJobs/publishingcredentials"
$publishingCredentials = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType `
$resourceType -ResourceName $resourceName -Action list -ApiVersion $Apiversion -Force
return $publishingCredentials
}
#Pulling authorization access token :
function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $AppServiceNameToDeployWebJobs) {
$publishingCredentials = Get-PublishingProfileCredentials $resourceGroupName $AppServiceNameToDeployWebJobs
return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f `
$publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}
$accessToken = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $AppServiceNameToDeployWebJobs
#Generating header to create and publish the Webjob :
$Header = #{
'Content-Disposition' = 'attachment; attachment; filename=Copy.zip'
'Authorization' = $accessToken
}
$apiUrl = "http://xxxx.scm.azurewebsites.net/app_data/jobs/triggered/Test/"
$result = Invoke-RestMethod -Uri $apiUrl -Headers $Header -Method put `
-InFile "D:\Work\WebJobs\WebJobsBuild\Test.zip" -ContentType 'application/zip' `
-TimeoutSec 600
The zip file size is only 43MB. How can I check the upper limit of file size allowed and how can I increase it? I've tried both Invoke-WebRequest and Invoke-RestMethod but the result is the same
I modify $apiUrl and it works for me.
It should be like
$apiUrl = "https://$AppServiceNameToDeployWebJobs.scm.azurewebsites.net/api/triggeredwebjobs/MyWebJob1"
Step 1. My test webjob in portal, and I will create MyWebJob1 later.
Step 2. Before running cmd.
Step 3. Modify the web job name as MyWebJob1.
Step 4. Check the webjob in portal.
Sample Code
$resourceGroupName='***';
$AppServiceNameToDeployWebJobs='jas***pp';
$Apiversion='2019-08-01';
#Function to get Publishing credentials for the WebApp :
function Get-PublishingProfileCredentials($resourceGroupName, $AppServiceNameToDeployWebJobs) {
$resourceType = "Microsoft.Web/sites/config"
$resourceName = "$AppServiceNameToDeployWebJobs/publishingcredentials"
$publishingCredentials = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType `
$resourceType -ResourceName $resourceName -Action list -ApiVersion $Apiversion -Force
return $publishingCredentials
}
#Pulling authorization access token :
function Get-KuduApiAuthorisationHeaderValue($resourceGroupName, $AppServiceNameToDeployWebJobs) {
$publishingCredentials = Get-PublishingProfileCredentials $resourceGroupName $AppServiceNameToDeployWebJobs
return ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f `
$publishingCredentials.Properties.PublishingUserName, $publishingCredentials.Properties.PublishingPassword))))
}
$accessToken = Get-KuduApiAuthorisationHeaderValue $resourceGroupName $AppServiceNameToDeployWebJobs
#Generating header to create and publish the Webjob :
$Header = #{
'Content-Disposition' = 'attachment; attachment; filename=test.zip'
'Authorization' = $accessToken
}
$apiUrl = "https://$AppServiceNameToDeployWebJobs.scm.azurewebsites.net/api/triggeredwebjobs/MyWebJob1"
$result = Invoke-RestMethod -Uri $apiUrl -Headers $Header -Method put `
-InFile "E:\test.zip" -ContentType 'application/zip' `
-TimeoutSec 600

Use Powershell to Publish to a WebApp Virtual Directory

I have an Azure WebApp, that is split into two virtual directories - UI and API.
I've managed to create the virtual directories in code, but cannot find a means of publishing to them.
Here's my code so far:
# Set UI Virtaul Directory (call /ui )
$website = Get-AzWebApp -Name $appsvWebAppName -ResourceGroupName $resourceGroupName
$VDApp = New-Object Microsoft.Azure.Management.WebSites.Models.VirtualApplication
$VDApp.VirtualPath = "/ui"
$VDApp.PhysicalPath = "site\wwwroot\ui"
$VDApp.PreloadEnabled ="YES"
$website.siteconfig.VirtualApplications.Add($VDApp)
$website | Set-AzWebApp -Verbose
# Set API Virtual Directory (call /api )
$website = Get-AzWebApp -Name $appsvWebAppName -ResourceGroupName $resourceGroupName
$VDApp = New-Object Microsoft.Azure.Management.WebSites.Models.VirtualApplication
$VDApp.VirtualPath = "/api"
$VDApp.PhysicalPath = "site\wwwroot\api"
$VDApp.PreloadEnabled ="YES"
$website.siteconfig.VirtualApplications.Add($VDApp)
$website | Set-AzWebApp -Verbose
$website.SiteConfig.VirtualApplications
# Dotnet publish & convert to zip here, removed for brevity ...
$uiZipPath = $zipFilesFolder + "\ui.zip"
$publishprofile = Get-AzWebAppPublishingProfile -ResourceGroupName $resourceGroupName `
-Name $appsvWebAppName `
-OutputFile $publishProfileFileName
Publish-AzWebApp -ArchivePath $uiZipPath `
-ResourceGroupName $resourceGroupName `
-Name $appsvWebAppName
I can't see how to point Publish-AzWebApp at a virtual directory.
The publish can be done manually, but I really want to automate it (using Publish-AzWebApp or another means).
How can I do this please?
The Publish-AzWebApp does not support that, you could use Kudu API in powershell to automate it.
In my sample, it uses VFS to create the directory first, then upload the zip file via Zip.
$appsvWebAppName = "xxxxxxx"
$resourceGroupName = "xxxxxxx"
$resource = Invoke-AzResourceAction -ResourceGroupName $resourceGroupName -ResourceType Microsoft.Web/sites/config -ResourceName "$appsvWebAppName/publishingcredentials" -Action list -ApiVersion 2018-02-01 -Force
$username = $resource.Properties.publishingUserName
$password = $resource.Properties.publishingPassword
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
$userAgent = "powershell/1.0"
# Create the folder, not lose `/` after `ui`
$apiUrl = "https://$appsvWebAppName.scm.azurewebsites.net/api/vfs/site/wwwroot/ui/"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method PUT
#Upload the zip file
$apiUrl = "https://$appsvWebAppName.scm.azurewebsites.net/api/zip/site/wwwroot/ui"
$filePath = "C:\Users\joyw\Desktop\testdep.zip"
Invoke-RestMethod -Uri $apiUrl -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -UserAgent $userAgent -Method PUT -InFile $filePath -ContentType "multipart/form-data"
For the site\wwwroot\api, it is the same logic, just change ui to api in the script.

Can you download an Azure Blob using Virtual Machine Identity Access Token through PowerShell without using Access Keys

I'm setting up a VM to do some bootstrapping on creation.
Part of this is to download a blob from an azure storage account to the VM.
These are all in the same subscription, resource group, etc.
I can do it this way fine:
function Get-BlobUsingVMIdentity
{
param(
[Parameter(Mandatory = $true)] $containerName,
[Parameter(Mandatory = $true)] $blobName,
[Parameter(Mandatory = $true)] $outputFolder
)
write-host "Defining package information"
mkdir $outputFolder -force
write-host "Getting Instance meta data"
$instanceInfo = Invoke-WebRequest -UseBasicParsing -Uri 'http://169.254.169.254/metadata/instance/?api-version=2018-02-01' `
-Headers #{Metadata="true"} `
| select -expand content `
| convertfrom-json `
| select -expand compute
$storageAccountName = "$($instanceInfo.resourceGroupName.replace('-rg',''))sa" # This is custom since we know our naming schema
$resourceGroupName = $($instanceInfo.resourceGroupName)
$subscriptionId = $($instanceInfo.subscriptionId)
write-host "Got storageAccountName [$storageAccountName], resourceGroupName [$resourceGroupName], subscriptionId [$subscriptionId]"
write-host "Getting VM Instance Access Token"
$response = Invoke-WebRequest -UseBasicParsing -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' `
-Headers #{Metadata = "true" }
$content = $response.Content | ConvertFrom-Json
$access_token = $content.access_token
write-host "Getting SAS Token From Storage Account"
$params = #{canonicalizedResource = "/blob/$($storageAccountName)/$($containerName)"; signedResource = "c"; signedPermission = "rcw"; signedProtocol = "https"; signedExpiry = "2031-09-23T00:00:00Z" }
$jsonParams = $params | ConvertTo-Json
$sasResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://management.azure.com/subscriptions/$($subscriptionId)/resourceGroups/$($resourceGroupName)/providers/Microsoft.Storage/storageAccounts/$($storageAccountName)/listServiceSas/?api-version=2017-06-01" `
-Method POST `
-Body $jsonParams `
-Headers #{Authorization="Bearer $access_token"}
$sasContent = $sasResponse.Content | ConvertFrom-Json
$sasCred = $sasContent.serviceSasToken
write-host "Manually download blob"
$params = #{signedResource = "c"; signedPermission = "rcw"; signedProtocol = "https"; signedExpiry = "2031-09-23T00:00:00Z" }
$jsonParams = $params | ConvertTo-Json
$sasResponse = Invoke-WebRequest -UseBasicParsing -Uri "https://$($storageAccountName).blob.core.windows.net/$($containerName)/$($blobName)?api-version=2017-06-01" `
-Method POST `
-Body $jsonParams `
-Headers #{Authorization="Bearer $access_token"}
$sasContent = $sasResponse.Content | ConvertFrom-Json
$sasCred = $sasContent.serviceSasToken
write-host "Setting up storage context"
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasCred
write-host "Downloading package"
Get-AzStorageBlobContent `
-Blob $blobName `
-Container $containerName `
-Destination $outputFolder `
-Context $ctx `
-Force
}
This works fine, except I have to grant full/write access to the identity in order for it to use the access key.
Is there a similar approach that would allow read only access to the blob?
My goals are:
1. No credentials stored anywhere
2. Download blob to VM from azure storage
3. No statically defined variables (ex: subscriptionid)
4. Read only access to the blob/storage account.
Appreciate any help!
According to my understanding, you want to use Azure VM MSI to access Azure storage. If so, please refer to the following steps:
Enable a system-assigned managed identity on a VM
Connect-AzAccount
$vm = Get-AzVM -ResourceGroupName myResourceGroup -Name myVM
Update-AzVM -ResourceGroupName myResourceGroup -VM $vm -AssignIdentity:$SystemAssigned
Grant your VM access to an Azure Storage container
Connect-AzAccount
$spID = (Get-AzVM -ResourceGroupName myRG -Name myVM).identity.principalid
New-AzRoleAssignment -ObjectId $spID -RoleDefinitionName "Storage Blob Data Reader" -Scope "/subscriptions/<mySubscriptionID>/resourceGroups/<myResourceGroup>/providers/Microsoft.Storage/storageAccounts/<myStorageAcct>/blobServices/default/containers/<container-name>"
access blob
# get AD access token
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' `
-Headers #{Metadata="true"}
$content =$response.Content | ConvertFrom-Json
$access_token = $content.access_token
# call Azure blob rest api
$url="https://<myaccount>.blob.core.windows.net/<mycontainer>/<myblob>"
$RequestHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$RequestHeader.Add("Authorization", "Bearer $access_token")
$RequestHeader.Add("x-ms-version", "2019-02-02")
$result = Invoke-WebRequest -Uri $url -Headers $RequestHeader
$result.content
Update
According to my test, when we get token to access Azure blob, we need to change resouce as https://storage.azure.com/
# get AD access token
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://storage.azure.com/' `
-Headers #{Metadata="true"}
$content =$response.Content | ConvertFrom-Json
$access_token = $content.access_token
# call Azure blob rest api
$url="https://<myaccount>.blob.core.windows.net/<mycontainer>/<myblob>"
$RequestHeader = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$RequestHeader.Add("Authorization", "Bearer $access_token")
$RequestHeader.Add("x-ms-version", "2019-02-02")
$result = Invoke-WebRequest -Uri $url -Headers $RequestHeader
$result.content
Update
Found a much easier way to do it!
The key was to use:
Connect-AzAccount -identity
This auto logs in a the identity and allows you to interact through AZ module instead of trying to hack API calls together.
You only need [Storage Blob Data Reader] assigned to the container to pull this off.
Working example looks like this:
function Get-BlobUsingVMIdentity
{
param(
[Parameter(Mandatory = $true)] $containerName,
[Parameter(Mandatory = $true)] $blobName,
[Parameter(Mandatory = $true)] $outputFolder
)
write-host "Get instance info"
$instanceInfo = Invoke-WebRequest -UseBasicParsing -Uri 'http://169.254.169.254/metadata/instance/?api-version=2018-02-01' `
-Headers #{Metadata="true"} `
| select -expand content `
| convertfrom-json `
| select -expand compute
$storageAccountName = "$($instanceInfo.resourceGroupName.replace('-rg',''))sa" # This is custom since we know our naming schema
write-host "Clear existing identies to keep cache fresh"
Clear-AzContext -force
write-host "Authenticate using identity"
$account = Connect-AzAccount -identity
if(-not $account.Context.Subscription.Id)
{
write-error "Failed to authenticate with identity. Ensure VM has identity enabled and is assigned the correct IAM roles"
return
}
write-host "Get storage context"
$ctx = New-AZStorageContext -StorageAccountName $storageAccountName
write-host "Getting blob"
Get-AzStorageBlobContent `
-Blob $blobName `
-Container $containerName `
-Destination $outputFolder `
-Context $ctx `
-Force
}
Get-BlobUsingVMIdentity `
-containerName "deploy" `
-blobName "deploy.zip" `
-outputFolder "c:\deploy\"

Resources