Powershell HTTP Post to Service Bus Queue returns 401 - azure

Trying to submit a message to a service bus queue I have set up, and keep getting a 401 Unauthorized return.
I've tried configuring the SAS token myself using this method
$ResourceGroupName = 'myResourceGroup'
$NameSpaceName = "serviceBusNameSpace"
$QueueName = "myQueueName"
$PolicyName = "RootManageSharedAccessKey"
$body = "test message"
$Namespace = (Get-AzServiceBusNamespace -ResourceGroupName $ResourceGroupName -Name $namespacename).Name
$key = (Get-AzServiceBusKey -ResourceGroupName $ResourceGroupName -Namespace $namespacename -Name $PolicyName).PrimaryKey
$origin = [DateTime]"1/1/1970 00:00"
$Expiry = (Get-Date).AddMinutes(5)
#compute the token expiration time.
$diff = New-TimeSpan -Start $origin -End $Expiry
$tokenExpirationTime = [Convert]::ToInt32($diff.TotalSeconds)
#Create a new instance of the HMACSHA256 class and set the key to UTF8 for the size of $Key
$hmacsha = New-Object -TypeName System.Security.Cryptography.HMACSHA256
$hmacsha.Key = [Text.Encoding]::UTF8.GetBytes($Key)
$scope = "https://$Namespace.servicebus.windows.net/"
#create the string that will be used when cumputing the hash
$stringToSign = [Web.HttpUtility]::UrlEncode($scope) + "`n" + $tokenExpirationTime
#Compute hash from the HMACSHA256 instance we created above using the size of the UTF8 string above.
$hash = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
#Convert the hash to base 64 string
$signature = [Convert]::ToBase64String($hash)
$fullResourceURI = "https://$Namespace.servicebus.windows.net/$QueueName"
#create the token
$token = [string]::Format([Globalization.CultureInfo]::InvariantCulture, `
"SharedAccessSignature sr={0}sig={1}&se={2}&skn={3}", `
[Web.HttpUtility]::UrlEncode($fullResourceURI), `
[Web.HttpUtility]::UrlEncode($signature), `
$tokenExpirationTime, $PolicyName)
$headers = #{ "Authorization" = "$token"; "Content-Type" = "application/atom+xml;type=entry;charset=utf-8" }
$uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages"
$headers.Add("BrokerProperties", "{}")
#Invoke-WebRequest call.
Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body -UseBasicParsing
I've also tried generating it through a built in cmdlet in Az.ServiceBus
$ResourceGroupName = 'myResourceGroup'
$NameSpaceName = "serviceBusNameSpace"
$QueueName = "myQueueName"
$PolicyName = "RootManageSharedAccessKey"
$body = "test message"
$expiry = (Get-Date).AddHours(2)
$authRule = Get-AzServiceBusAuthorizationRule -ResourceGroupName $ResourceGroupName -Namespace $NamespaceName
$token = New-AzServiceBusAuthorizationRuleSASToken -AuthorizationRuleId $authRule.Id -KeyType Primary -ExpiryTime $Expiry
$headers = #{ "Authorization" = "SharedAccessSignature $($token.SharedAccessSignature)"; "Content-Type" = "application/atom+xml;type=entry;charset=utf-8" }
$uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages"
$headers.Add("BrokerProperties", "{}")
#Invoke-WebRequest call.
Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body -UseBasicParsing
Both give me a 401 unauthorized error
Invoke-WebRequest : The remote server returned an error: (401) Unauthorized.
At line:9 char:17
+ ... $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Pos ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
I'm not sure what else to do. Is there a setting I need to configure for my queue within the azure portal?
Have found solution. UTC time was originally expiring token before even sending, in addition to malformed SAS signature
Final code edit below
$key = (Get-AzServiceBusKey -ResourceGroupName $ResourceGroupName -Namespace $namespacename -Name $PolicyName).PrimaryKey
$origin = [DateTime]"1/1/1970 00:00"
$Expiry = (Get-Date).AddMinutes(20)
$Expiry = $Expiry.ToUniversalTime()
#compute the token expiration time.
$diff = New-TimeSpan -Start $origin -End $Expiry
$tokenExpirationTime = [Convert]::ToInt32($diff.TotalSeconds)
$uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages"
$scope = "https://$Namespace.servicebus.windows.net/$QueueName"
#create the string that will be used when cumputing the hash
$stringToSign = [Web.HttpUtility]::UrlEncode($scope) + "`n" + $tokenExpirationTime
#Create a new instance of the HMACSHA256 class and set the key to UTF8 for the size of $Key
$hmacsha = New-Object -TypeName System.Security.Cryptography.HMACSHA256
$hmacsha.Key = [Text.Encoding]::UTF8.GetBytes($Key)
#Compute hash from the HMACSHA256 instance we created above using the size of the UTF8 string above.
$hash = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
#Convert the hash to base 64 string
$signature = [Convert]::ToBase64String($hash)
#create the token
$token = [string]::Format([Globalization.CultureInfo]::InvariantCulture, `
"SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", `
[Web.HttpUtility]::UrlEncode($scope), `
[Web.HttpUtility]::UrlEncode($signature), `
$tokenExpirationTime, $PolicyName)
$headers = #{ "Authorization" = "$token"}
$headers.Add("Content-Type", "application/atom+xml;type=entry;charset=utf-8")
#Invoke-WebRequest call.
Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body -UseBasicParsing

I have made some changes in your script and it is working fine.
$ResourceGroupName = 'myResourceGroup'
$Namespace = "serviceBusNameSpace"
$QueueName = "myQueueName"
$PolicyName = "RootManageSharedAccessKey"
$body = "test message"
$key = (Get-AzServiceBusKey -ResourceGroupName $ResourceGroupName -Namespace $Namespace -Name $PolicyName).PrimaryKey
$origin = [DateTime]"1/1/1970 00:00"
$Expiry = (Get-Date).AddMinutes(5)
#compute the token expiration time.
$diff = New-TimeSpan -Start $origin -End $Expiry
$tokenExpirationTime = [Convert]::ToInt32($diff.TotalSeconds)
#Create a new instance of the HMACSHA256 class and set the key to UTF8 for the size of $Key
$hmacsha = New-Object -TypeName System.Security.Cryptography.HMACSHA256
$hmacsha.Key = [Text.Encoding]::UTF8.GetBytes($Key)
#create the string that will be used when cumputing the hash
$stringToSign = [Web.HttpUtility]::UrlEncode($Namespace) + "`n" + $tokenExpirationTime
#Compute hash from the HMACSHA256 instance we created above using the size of the UTF8 string above.
$hash = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
#Convert the hash to base 64 string
$signature = [Convert]::ToBase64String($hash)
#create the token
$token = [string]::Format([Globalization.CultureInfo]::InvariantCulture, `
"SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", `
[Web.HttpUtility]::UrlEncode($Namespace), `
[Web.HttpUtility]::UrlEncode($signature), `
$tokenExpirationTime, $PolicyName)
$headers = #{ "Authorization" = "$token"; "Content-Type" = "application/atom+xml;type=entry;charset=utf-8" }
$uri = "https://$Namespace.servicebus.windows.net/$QueueName/messages"
$headers.Add("BrokerProperties", "{}")
#Invoke-WebRequest call.
Invoke-WebRequest -Uri $uri -Headers $headers -Method Post -Body $body -UseBasicParsing
The changes which I have made are:
You don't need to create scope variable. You need to pass the $Namespace to stringToSign.
You don't need to use Get-AzServiceBusNamespace to get namespace name as you are already taking this as user input.

See post edit.
Token expiration time wasn't converted to UTC, making it always expired, in addition to not having the SaS token configuration string formed correctly.

Related

Azure consumption api returning blank value

I am trying to use the azure consumption api to get usage details for my subscription, but it keeps returning a blank array. I know there should be something there.
$VaultName =
$KeyName =
$tenantId =
$applicationId =
$SubscriptionId =
$apiEndpointUri = "https://management.azure.com/"
$secret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $KeyName -AsPlainText
$RequestAccessTokenUri = "https://login.microsoftonline.com/$tenantId/oauth2/token"
$body = "grant_type=client_credentials&client_id=$($applicationId)&client_secret=$($secret)&resource=$($apiEndpointUri)"
$AccessToken = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType $contentType
$apiuri = "https://management.azure.com/subscriptions/$($SubscriptionId)/providers/Microsoft.Consumption/usageDetails?$filter={reportedstartTime=2022-03-01&reportedendTime=2022-04-01}&api-version=2021-10-01"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Authorization", "Bearer $($AccessToken.access_token)")
Invoke-RestMethod -Uri $apiuri -Headers $headers | ConvertTo-Json
This is the response every time
{
"value": [
]
}
I have tried everything I can think of to change the $apiuri around. And I know all the auth parts are correct.

Add redirectUris for Azure application from PowerShell

This answer describes how to set a redirectUri to an Azure application using the Azure CLI:
az rest `
--method PATCH `
--uri 'https://graph.microsoft.com/v1.0/applications/{id}' `
--headers 'Content-Type=application/json' `
--body "{spa:{redirectUris:['http://localhost:3000']}}"
That request will overwrite the current list of redirect URIs. How can I add an item to the list instead?
We can use this below Powershell script to do the same using App Registration object id, client id and secrets .
$url = "http://localhost:4000"
$objectId = "<objectid>"
$clientId = "<clientID>"
$tenantValue ="<tenantID>"
$clientSecret ="<client secret value>"
$serviceAccountEmail = "yourusername.onmicrosoft.com"
$serviceAccountPassword = "your password"
$webServiceURL = $url
Write-Host "$webServiceURL"
Write-Host "Done creating the webServiceURL"
Write-Host "Convert password to Secure string"
$SecurePassword = ConvertTo-SecureString $serviceAccountPassword -AsPlainText -Force
Write-Host "Done converting password to Secure string"
$Credential = New-Object System.Management.Automation.PSCredential($serviceAccountEmail, $SecurePassword)
Write-Host "Logging in"
Login-AzAccount -Credential $Credential
$tid = (Get-AzTenant).Id
Write-Host "Getting token"
$tokenBody = #{
'tenant' = $tid
'client_id' = $clientId
'scope' = 'https://graph.microsoft.com/.default'
'client_secret' = $clientSecret
'grant_type' = 'client_credentials'
}
$Params = #{
'Uri' = "https://login.microsoftonline.com/$tid/oauth2/v2.0/token"
'Method' = 'Post'
'Body' = $tokenBody
'ContentType' = 'application/x-www-form-urlencoded'
}
$AuthResponse = Invoke-RestMethod #Params
$AuthResponse
$header = #{
'Content-Type' = 'application/json'
'Authorization' = "Bearer $($AuthResponse.access_token)"
}
$header
$redirectUris = (Invoke-RestMethod -Method Get -Uri "https://graph.microsoft.com/beta/applications/$objectId" -Headers $header).spa.redirectUris
if ($redirectUris -notcontains "$webServiceURL") {
$redirectUris += "$webServiceURL"
Write-Host "Adding $webServiceURL to redirect URIs";
}
$body = #{
'spa' = #{
'redirectUris' = $redirectUris
}
} | ConvertTo-Json
Invoke-RestMethod -Method Patch -Uri "https://graph.microsoft.com/beta/applications/$objectId" -Headers $header -Body $body
Here is the OUTPUT for Reference:-
You can fetch the current values with --method get, convert it to an ArrayList and then add your new value:
$appdata = az rest --method get --uri 'https://graph.microsoft.com/v1.0/applications/{id}' | ConvertFrom-Json
$uris = [System.Collections.ArrayList]$appdata.web.redirectUris
$uris.Add('abc')

MAC Signature Not Same Error While Accessing Storage REST API

I trying to call the storage REST API using powershell but facing errors. PFB details-
Script
$version = "2017-04-17"
$storageAccount = "{storageAccountName}"
$accesskey= “{storageAccountAccessKey}"
$resource = "?comp=list"
$storage_url = "https://$storageAccount.blob.core.windows.net/$resource"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$stringToSign = "GET`nx-ms-date:$GMTTime`nx-ms-version:2017-04-17`n/$storageAccount/$resource"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)
$headers = #{
'x-ms-date' = $GMTTime
'Authorization' = "SharedKeyLite " + $storageAccount + ":" + $signature
'x-ms-version' = $version
}
Invoke-RestMethod -Method GET -Uri $storage_url -Headers $headers
Error Message
Invoke-RestMethod : AuthenticationFailedServer failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the
signature.
RequestId:ee5459f7-501e-004b-0426-46de36000000
Time:2020-06-19T10:45:36.7846714ZThe MAC signature found in the HTTP request <signature>' is not the same as any computed signature.
Server used following string to sign: 'GET
x-ms-date:Fri, 19 Jun 2020 10:37:00 GMT
x-ms-version:2017-04-17
/<storageAccount>/?comp=list'.
Can someone please help me understand what is missing here.
Documentation being followed for authorization- https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key
Please use the following code:
$version = "2017-04-17"
$storageAccount = "{storageAccountName}"
$accesskey= “{storageAccountAccessKey}"
$resource = "?comp=list"
$storage_url = "https://$storageAccount.blob.core.windows.net/$resource"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$ContentMd5 = ""
$ContentType = ""
$CanonicalizedHeaders = "x-ms-date:$GMTTime`nx-ms-version:$version`n"
$CanonicalizedResource = "/$storageAccount/$resource"
$stringToSign = "GET`n$ContentMd5`n$ContentType`n`n$CanonicalizedHeaders$CanonicalizedResource"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)
$headers = #{
'x-ms-date' = $GMTTime
'Authorization' = "SharedKeyLite " + $storageAccount + ":" + $signature
'x-ms-version' = $version
}
Invoke-RestMethod -Method GET -Uri $storage_url -Headers $headers

How to do az acr import with Az powershell?

If Az powershell does not have it, then a working code sample using the REST Api would be helpful.
This is the path I am going to pursue, but if someone has a working sample - please share.
It's impossible to import images to ACR through the Azure PowerShell, but the REST API exists. Take a look at the Import Image REST API.
So I managed to trigger the import using REST API. Here is my code (using a Service Principal to login):
function Get-AcrId($SubId, $RGName, $AcrName)
{
"/subscriptions/$SubId/resourceGroups/$RGName/providers/Microsoft.ContainerRegistry/registries/$ACRName"
}
function Get-AzureAuthenticationToken(
[Parameter(Mandatory)][String]$TenantID,
[Parameter(Mandatory)][String]$ClientID,
[Parameter(Mandatory)][String]$ClientSecret,
[Parameter(Mandatory)][String]$ResourceAppIDUri)
{
$tokenResponse = Invoke-RestMethod -Method Post -UseBasicParsing `
-Uri "https://login.windows.net/$TenantID/oauth2/token" `
-Body #{
resource = $ResourceAppIDUri
client_id = $ClientID
grant_type = 'client_credentials'
client_secret = $ClientSecret
} -ContentType 'application/x-www-form-urlencoded'
Write-Verbose "Access token type is $($tokenResponse.token_type), expires $($tokenResponse.expires_on)"
$tokenResponse.access_token
}
function Import-DockerImage(
[Parameter(Mandatory)]$SourceSubId,
[Parameter(Mandatory)]$SourceRGName,
[Parameter(Mandatory)]$SourceACRName,
[Parameter(Mandatory)]$TargetSubId,
[Parameter(Mandatory)]$TargetRGName,
[Parameter(Mandatory)]$TargetACRName,
[Parameter(Mandatory)]$ImageName,
[Parameter(Mandatory)]$ImageTag
)
{
$AzContext = Get-AzContext
if (!$AzContext)
{
throw "No Az context is found."
}
$TenantId = $AzContext.Tenant.Id
$ClientId = $AzContext.Account.Id
$ClientSecret = $AzContext.Account.ExtendedProperties.ServicePrincipalSecret
$token = Get-AzureAuthenticationToken -TenantID $TenantId -ClientID $ClientId -ClientSecret $ClientSecret -ResourceAppIDUri "https://management.core.windows.net/"
$url = "https://management.azure.com$(Get-AcrId $TargetSubId $TargetRGName $TargetACRName)/importImage?api-version=2019-05-01"
$body = #{
source = #{
resourceId = Get-AcrId $SourceSubId $SourceRGName $SourceACRName
sourceImage = "${ImageName}:$ImageTag"
}
targetTags = #(
"${ImageName}:$ImageTag"
)
mode = "NoForce"
} | ConvertTo-Json -Depth 99
$headers = #{
"Authorization" = "Bearer $token"
"Content-Type" = "application/json"
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$r = Invoke-WebRequest $url -Method Post -Headers $headers -Body $body
$headers.Remove('Content-Type')
while ($r.StatusCode -eq 202)
{
$RetryAfter = $r.Headers.'Retry-After'
$Location = $r.Headers.Location
Start-Sleep -Seconds $RetryAfter
$r = Invoke-WebRequest $Location -Headers $headers
}

Can I list all items of an Azure table(Azure Table Storage) just by PartitionKey?

I tried to use the below uri for REST call, but getting error(403 Forbidden)
https://$storageAccount.table.core.windows.net/$tableName()?$filter=PartitionKey%20eq%20'Key1'
Is there other way? Please help.
According to my test, we can use share key to call the Azure table rest api
$accesskey="<storage account key>"
$storageAccount = "<account name>"
$version = "2017-04-17"
$resource = "table name"
$key="Jim"
$table_url = "https://$storageAccount.table.core.windows.net/$($resource)?`$filter=PartitionKey%20eq%20'$($key)'"
# create share key
$GMTTime = (Get-Date).ToUniversalTime().AddYears(1).toString('R')
$stringToSign = "$GMTTime`n/$storageAccount/$resource"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Convert]::FromBase64String($accesskey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
$signature = [Convert]::ToBase64String($signature)
$headers = #{
'x-ms-date' = $GMTTime
"Authorization" = "SharedKeyLite " + $storageAccount + ":" + $signature
"x-ms-version" = $version
"Accept" = "application/json"
}
$item = Invoke-RestMethod -Method GET -Uri $table_url -Headers $headers -ContentType application/json
$item.value
Update
Regarding how to create sas token via Azure Portal, please refer to the following steps
Create sas token
Test
GET https://myaccount.table.core.windows.net/mytable()
?$filter=<>
&sv=2019-02-02&ss=t&srt=o&sp=r&se=2020-03-27T13:01:24Z&st=2020-03-27T05:01:24Z&spr=https&sig=OFUNXShu6kTojIp3SU...TkG%2BXAVZXJ8sqc%3D

Resources