Enable-AzStorageStaticWebsite command in Azure RM PowerShell module - azure

I am trying to create an automated script to create and deploy static website to Azure Storage Blob.
However, I still have to use Azure.Storage module instead of Az.Storage. Is there an equivalent Cmdlet for Enable-AzStorageStaticWebsite in Azure RM?

Unfortunately, there is not an equivalent command in Azure.Storage(GA) module nowadays, you could use Enable-AzureStorageStaticWebsite with Azure.Storage 4.4.1-preview, but the Az module is a new powershell module also for Azure Resource Manager, may be you could have a try.
If you would like to run scripts developed for AzureRM using Az, use the Enable/Disable-AzureRmAlias cmdlets to add or remove aliases from AzureRM cmdlets to Az cmdlets.
For more details, refer to this link.

I would highly encourage using the Az module for this. IN AzureRM, Static websites is a preview in Azure.Storage: https://www.powershellgallery.com/packages/Azure.Storage/4.4.1-preview

You can also call it yourself:
#function to Enable static website for the Azure Storage account.
Function Enable-AzureRmStorageStaticWebsite(
[Microsoft.Azure.Commands.Common.Authentication.Abstractions.IStorageContext] [Parameter(Mandatory = $true)] $Context,
[string] [Parameter(Mandatory = $true)] $IndexDocument,
[string] [Parameter(Mandatory = $true)] $ErrorDocument404Path
) {
$sasToken = New-AzureStorageAccountSASToken -Context $Context `
-Service Blob -ResourceType Service -Protocol HttpsOnly -Permission wla `
-StartTime (Get-Date).AddHours(-1) -ExpiryTime (Get-Date).AddHours(4)
$body = (#'
<?xml version="1.0" encoding="utf-8"?>
<StorageServiceProperties>
<StaticWebsite>
<Enabled>true</Enabled>
<IndexDocument>{0}</IndexDocument>
<ErrorDocument404Path>{1}</ErrorDocument404Path>
</StaticWebsite>
</StorageServiceProperties>
'# -f $IndexDocument, $ErrorDocument404Path)
$headers = #{"x-ms-version" = "2018-03-28"; "x-ms-date" = (Get-Date -Format R); "Content-Type" = "application/xml"; "Content-Length" = [string]$body.length }
$apiUrl = ("{0}{1}&restype=service&comp=properties" -f $Context.BlobEndPoint, $sasToken)
Write-Verbose ('Enable-AzureRmStorageStaticWebsite -IndexDocument {0} -ErrorDocument404Path {1}' -f $IndexDocument, $ErrorDocument404Path)
Invoke-RestMethod -Method Put -Uri $apiUrl -Headers $headers -Body $body
}
Please assure you have installed module Azure.Storage
supporting the storage api-version '2018-03-28' (I believe powershell-version: 4.4.1 or higher)

Related

Undeleting a Soft Deleted Blob in Azure Storage Using a REST API call from PowerShell

I am trying to create a script to retrieve blobs for a given customer number from a storage account in Azure. All blobs reside in a single container, with 'actioned' blobs being soft deleted.
I can use PowerShell to display the relevant blobs, including their 'IsDeleted' status, but I understand that PowerShell doesn't have the necessary command to undelete blobs and so I'm trying to make a REST API call from the PowerShell script.
I do an inital login to the Azure platform and set a variable for an SAS token (which includes the necessary permissions to undelete):
$username = "<myUserName>"
$encryptedPwd = Get-Content <path\securepassword.txt> | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PsCredential($username, $encryptedPwd)
$strgaccname = "<myStorageAccount>"
$strgcontainer = "<myContainer>"
#SAS Token
$sastkn = "<mySAStoken>"
#Set StorageContext
$ctx = New-AzStorageContext -StorageAccountName $strgaccname -SasToken $sastkn
$subId = "mySubscriptionID"
Connect-AzAccount -Credential $cred -Subscription $subID
I can list all matching blobs with the following PowerShell:
$searchstring = '*'+<myCustomerNumber>+'*'
Get-AzStorageBlob -Blob $searchstring -Context $ctx -Container $strgcontainer -IncludeDeleted `
| Select-Object Name, Length, LastModified, IsDeleted `
| Sort-Object LastModified -Descending
I am unsure how to proceed with the REST API call. Looking at some other people's methods, I have something like the following, using a test blob that has been soft deleted:
$uri = "https://<myStorageAccount>.blob.core.windows.net/<myContainer>/<myTestBlob>?comp=undelete"
$headers = #{
'Authorization' = "Bearer <accessToken>";
'x-ms-date' = $((get-date -format r).ToString());
'x-ms-version' = "2020-12-06";
}
Invoke-RestMethod -Method 'Put' -Uri $uri -Headers $headers
However, I don't know how to create the Bearer Access Token that is mentioned.
We have done a repro in our local environment & it is working fine, Below statements are based on our analysis.
You can use the below Powershell script which will help you in restoring the soft-deleted blobs in your storage account.
Here is the Powershell Script :
Connect-AzAccount
#Get all deleted blob within a container
$StorageAccount = Get-AzStorageAccount | Where-Object { $_.StorageAccountName -eq "<storageAccountName>" }
$Blobs = Get-AzStorageContainer -Name "<ContainerName>" -Context $StorageAccount.Context | Get-AzStorageBlob -IncludeDeleted
$DeletedBlobs=$($Blobs| Where-Object {$_.IsDeleted -eq $true})
#Get your Bearer access token
$resource = “https://storage.azure.com"
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext
$accessToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, $context.Environment, $context.Tenant.Id.ToString(), $null, [Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, $null, $resource).AccessToken
#Restore
foreach ($DeletedBlob in $DeletedBlobs) {
Write-Host "Restoring : $($DeletedBlob.Name)"
$uri = "$($DeletedBlob.BlobBaseClient.Uri.AbsoluteUri)?comp=undelete"
$headers = #{
'Authorization' = "Bearer $accessToken";
'x-ms-date' = $((get-date -format r).ToString());
'x-ms-version' = "2020-12-06";
}
Invoke-RestMethod -Method 'Put' -Uri $uri -Headers $headers
}
Here is the Sample output for your reference:
Note:
In order to perform the restoration of soft-deleted blob, you need to have a Storage Blob Data Contributor RBAC role on the Storage Account.

How to make an Azure app registration with platform SPA via Powershell

We use PowerShell to set up an Azure deployment, which, among other Azure resources, creates an app registration.
The simplified code is as follows:
$appRegistration = New-AzADApplication `
-DisplayName $applicationName `
-HomePage "$webAppUrl" `
-IdentifierUris "api://$webAppName";
To it, we add redirect uris, like this:
if ($redirectUris -notcontains "$webAppUrl") {
$redirectUris.Add("$webAppUrl");
Write-Host "Adding $webAppUrl to redirect URIs";
}
if ($redirectUris -notcontains "$webAppUrl/aad-auth") {
$redirectUris.Add("$webAppUrl/aad-auth");
Write-Host "Adding $webAppUrl/aad-auth to redirect URIs";
}
Update-AzADApplication `
-ApplicationId $applicationId `
-IdentifierUris "api://$applicationId" `
-ReplyUrl $redirectUris | Out-Null
This works great, and an app registration with the "web" platform is created. It looks like this:
My question is how can we get these redirect uris to be under the "SPA" platform, using PowerShell? Like in the image below, which was done manually on the Portal.
Looks there is no feature in the built-in command to do that, you could call the MS Graph - Update application in the powershell directly.
You could refer to the sample below work for me, make sure your service principal/user acount logged in Az via Connect-AzAccount has the permission to call the API.
$objectId = "xxxxxxxxxxxxxxxx"
$redirectUris = #()
$webAppUrl = "https://joyweb.azurewebsites.net"
if ($redirectUris -notcontains "$webAppUrl") {
$redirectUris += "$webAppUrl"
Write-Host "Adding $webAppUrl to redirect URIs";
}
if ($redirectUris -notcontains "$webAppUrl/aad-auth") {
$redirectUris += "$webAppUrl/aad-auth"
Write-Host "Adding $webAppUrl/aad-auth to redirect URIs";
}
$accesstoken = (Get-AzAccessToken -Resource "https://graph.microsoft.com/").Token
$header = #{
'Content-Type' = 'application/json'
'Authorization' = 'Bearer ' + $accesstoken
}
$body = #{
'spa' = #{
'redirectUris' = $redirectUris
}
} | ConvertTo-Json
Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/v1.0/applications/$objectId" -Headers $header -Body $body
Check the result in the portal:
There was a similar thread where someone was trying to programmatically add the redirect URIs for SPA and could not do it because it defaults under the Web section.
He was able to resolve this by posting with Azure CLI to the Graph API:
az rest `
--method PATCH `
--uri 'https://graph.microsoft.com/v1.0/applications/{id}' `
--headers 'Content-Type=application/json' `
--body "{spa:{redirectUris:['http://localhost:3000']}}"

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\"

Forced Conversion from AzureRM to AZ powershell

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'

Why does certain Azure CLI commands require az login

In our VSTS release pipeline we want to call a powershell script that adds a function key for one of my Azure Functions (using the Key Management rest API).
I've created a script based on this article:
https://www.markheath.net/post/managing-azure-function-keys
Param(
[string] [Parameter(Mandatory=$true)] $ResourceGroup,
[string] [Parameter(Mandatory=$true)] $AppName,
[string] [Parameter(Mandatory=$true)] $FunctionName,
[string] [Parameter(Mandatory=$true)] $KeyName,
[string] [Parameter(Mandatory=$true)] $KeyValue
)
function getAuthenticationToken([string]$appName, [string]$resourceGroup)
{
$user = az webapp deployment list-publishing-profiles -n $appName -g $resourceGroup `
--query "[?publishMethod=='MSDeploy'].userName" -o tsv
$pass = az webapp deployment list-publishing-profiles -n $appName -g $resourceGroup `
--query "[?publishMethod=='MSDeploy'].userPWD" -o tsv
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$jwt = Invoke-RestMethod -Uri "https://$appName.scm.azurewebsites.net/api/functions/admin/token" -Headers #{Authorization=("Basic {0}" -f $encodedCreds)} -Method GET
return $jwt
}
function setFunctionKey([string]$appName, [string]$functionName, [string] $keyName, [string]$keyValue, [string]$jwt)
{
$body = (#{
"name" = $keyName
"value" = $keyValue
} | ConvertTo-Json)
#Setting the SecurityProtocol is a workaround for calling Azure APIs, I think?
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
Invoke-RestMethod -Uri "https://$appName.azurewebsites.net/admin/functions/$functionName/keys/$keyName/" `
-Headers #{Authorization=("Bearer $jwt")} `
-Method PUT `
-ContentType "application/json" `
-Body $body
} catch {
$_.Exception | Format-List -Force
}
}
$jwt = getAuthenticationToken $AppName $ResourceGroup
setFunctionKey $AppName $FunctionName $KeyName $KeyValue $jwt
Write-Host "Specified key '$KeyName' has been added to $FunctionName"
Works locally, but when running it VSTS it gets and error when calling
$user = az webapp deployment list-publishing-profiles -n $appName -g $resourceGroup `
--query "[?publishMethod=='MSDeploy'].userName" -o tsv
with the message:
ERROR: Please run 'az login' to setup account.
We have other azure cli calls that works, ex: az cosmosdb database, so I guess our Service Principle connections are in place. What could be the issue here?
Seems like we had an old powershell version containing a bug that keeps the old service connection authentication context when you create a new service connection authentication, which I did in this case.
So we updated powershell on our build agents and we got things going!
From the document that you posted above, there is something you could miss.
The first step is that we need to get the credentials to call the Kudu
API. If you're authenticated with the Azure CLI, you can do that by
calling the az webapp deployment list-publishing-profiles command and
extracting the userName and userPWD for MSDeploy. You need to provide
the function app name and resource group name.
It seems that you should authenticate the Azure CLI before you use them.

Resources