Azure Batch REST API, authorization issue - azure

I've tried to authenticate to Azure Batch using REST API, to do so I wrote following PowerShell code
$Key = 'key'
$region = "region"
$sharedKey = [System.Convert]::FromBase64String($Key)
$date = [System.DateTime]::UtcNow.ToString("R")
$stringToSign = "GET`n`n`n`n`n`n`n`n`n`n`n`nocp-date:$date`n /$batchAccount/jobs`napi-version:2019-08-01.10.0`ntimeout:20"
[byte[]]$dataBytes = ([System.Text.Encoding]::UTF8).GetBytes($stringToSign)
$hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha256.Key = [Convert]::FromBase64String($key)
$sig = [Convert]::ToBase64String($hmacsha256.ComputeHash($dataBytes))
$authhdr = "SharedKey $BatchAccount`:$sig"
$headers = #{
"ocp-date" = $date;
"Authorization" = "$authhdr";
}
Invoke-restmethod -Headers $headers -Uri 'https://$BatchAccount.$region.batch.azure.com/jobs?api-version=2019-08-01.10.0'
please note that I know that I can
use OAuth2 as alternative authentication mechanism
use Az.Batch powershell modules
I just wanted to do this using REST and SharedKey scheme as described here
https://learn.microsoft.com/en-us/rest/api/batchservice/authenticate-requests-to-the-azure-batch-service
for this API
https://learn.microsoft.com/en-us/rest/api/batchservice/job/list
But for some reason it doesn't work
I get this error but everything seems to be folowing the docs
"message":{
"lang":"en-US",
"value":"Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.\nRequestId:eb5134f2-2821-4244-ac5b-066bf19bec10\nTime:2019-11-24T21:08:20.3223384Z"
},
"values":[
{
"key":"AuthenticationErrorDetail",
"value":"The MAC signature found in the HTTP request 'signature-goes-here' is not the same as any computed signature. Server used following string to sign: 'GET\n\n\n\n\napplication/json; odata=minimalmetadata; charset=utf-8\n\n\n\n\n\n\nocp-date:Sun, 24 Nov 2019 21:08:20 GMT\n/name-goes-here/jobs\napi-version:2019-08-01.10.0'."
}
]

There is something wrong with $stringToSign . Try this :
$Key = "your key"
$region = "your region"
$BatchAccount = "your account name"
$BatchAccountURL = "Https://$BatchAccount.$region.batch.azure.com"
$sharedKey = [System.Convert]::FromBase64String($Key)
$date = [System.DateTime]::UtcNow.ToString("R")
$stringToSign = "GET`n`n`n`n`n`n`n`n`n`n`n`nocp-date:$date`n/$BatchAccount/jobs`napi-version:2019-08-01.10.0"
[byte[]]$dataBytes = ([System.Text.Encoding]::UTF8).GetBytes($stringToSign)
$hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha256.Key = [Convert]::FromBase64String($key)
$sig = [Convert]::ToBase64String($hmacsha256.ComputeHash($dataBytes))
$authhdr = "SharedKey $BatchAccount`:$sig"
$headers = #{
"ocp-date" = $date;
"Authorization" = "$authhdr";
}
Invoke-restmethod -Headers $headers -Uri "$BatchAccountURL/jobs?api-version=2019-08-01.10.0"
Result :

Related

Using Azure Yaml pipeline to call powershell RestMethod with secret variable in body

I am trying to automate a step in our Azure yaml pipeline to delete some users from Auth0 using PowerShell and RestMethod, but for some reason the secret is either not used correctly or changed or something.
I have set the secret as a variable and then tried using it either directly in the script or through setting it on the task as well as an environment variable, but no luck.
Here is my last iteration, but still no joy, could anyone shed some light on the issue?
$api = "api/v2/"
$audience = $audience + $api
$secret = "$env:AUTH0_CLIENT_SECRET"
$test = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($secret))
Write-Host $clientSecret.substring(0,10)
Write-Host $test.substring(0,10)
Write-Host $audience
Write-Host $clientId
$Body = #{
client_id = $clientId
client_secret = $test OR $secret OR $clientSecret
audience = $audience
grant_type = "client_credentials"
}
Write-Host 'Getting token'
$response = Invoke-RestMethod https://mytenant.eu.auth0.com/oauth/token `
-Method 'POST' `
-ContentType 'application/json; charset=utf-8' `
-Body ($Body | ConvertTo-Json)
As shown, I have tried using test, secret and clientSecret as the secret, but also no luck. The write host at the start does show the correct starting characters for $clientSecret
Am I missing something basic or is this just not possible using RestMethod?
Ended up going scorched earth and completely deleted the pipelines and started again, this is the powershell script I ended up with that worked.
$token = "/oauth/token"
$https = "https://"
$api = "/api/v2/"
$audience = $https + $domain + $api
$tokenUrl = $https + $domain + $token
Write-Host 'Getting token'
$response = Invoke-RestMethod $tokenUrl `
-Method 'POST' `
-ContentType 'application/json; charset=utf-8' `
-Body (#{
client_id = $clientId
client_secret = "$env:AUTH0_CLIENT_SECRET"
audience = $audience
grant_type = "client_credentials"
} | ConvertTo-Json)

Azure REST API list key vault secrets has maxresults limited to 25

I'm trying to get a list of all the secrets in each of my key vaults and I'm using Microsofts documentation at this URL.
https://learn.microsoft.com/en-us/rest/api/keyvault/getsecrets/getsecrets#secretlistresult
It states that if you do not set maxresults it will default to 25. It does however throw this error in my powershell script when i try to set it higher than 25.
{"error":{"code":"BadParameter","message":"invalid maxresults"}}
From the documentation the endpoint does not appear to contain any pagination or way to get more than 25 random secrets. This seems to make the endpoint pretty useless as there are no ways to filter the listings.
The command I'm using to get the list is this.
$uri = ""https://$($Vault).vault.azure.net/secrets?api-version=7.1&maxresults=26""
Invoke-RestMethod -Method Get -Uri $uri -Headers $headers
As Gaurav said in the comment, you need to use nextLink to get the result of next page.
There is my test code with do-until:
$LoginUrl = "https://login.microsoft.com"
$RresourceUrl = "https://vault.azure.net/.default"
$ClientID = ""
$ClientSecret = ""
$TenantName = ""
# Compose REST request.
$Body = #{ grant_type = "client_credentials"; scope = $RresourceUrl; client_id = $ClientID; client_secret = $ClientSecret }
$OAuth = Invoke-RestMethod -Method Post -Uri $LoginUrl/$TenantName/oauth2/v2.0/token -Body $Body
# Check if authentication was successfull.
if ($OAuth.access_token) {
# Format headers.
$HeaderParams = #{
'Content-Type' = "application\json"
'Authorization' = "Bearer $($OAuth.access_token)"
}
# Create an empty array to store the result.
$QueryResults = #()
$Uri = "https://<your-key-vault-name>.vault.azure.net/secrets?api-version=7.1"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-RestMethod -Headers $HeaderParams -Uri $Uri -UseBasicParsing -Method "GET" -ContentType "application/json"
$Results.nextLink
if ($Results.value) {
$QueryResults += $Results.value
}
else {
$QueryResults += $Results
}
$Uri = $Results.nextLink
} until (!($Uri))
# Return the result.
$QueryResults
}
else {
Write-Error "No Access Token"
}

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

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

How to make Invoke-RestMethod GET and PUT requests to Azure table storage using SAS key

This is a two-part question. I am in the process of automating tasks that a) require information from my Azure table and b) need to update specific entities in my Azure table. I've currently been able to accomplish this by using either of the 2 provided access keys but think this is an unsafe practice and want to define individual policies for different groups and so want to transition into using generated SAS keys.
a) I can currently use SAS policies to retrieve the whole table and find the information I need but I think a better method is to perform an individual query that only pulls the single entity that matches a specific property I'm looking for (e.g. pull all properties of an entity that matches a customer ID: "000000001"). How can I change my code to accomplish this?
$tableName = "accountTD"
$sasReadToken = '<SAS token here>'
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$finalResult = Invoke-WebRequest -Uri $tableUri -Headers $header -UseBasicParsing
$finalResult = $finalResult.Content | ConvertFrom-Json
$finalResult.value
b) I also need to update the same entity in the table and can't seem to figure out how to authorize it with my generated SAS key. I'm not sure whether to use Invoke-WebRequest or Invoke-RestMethod or how to go about either of them. Here's what I have so far based on my research.
function addUpdateEntity ($tableName, $PartitionKey, $RowKey, $entity){
$sasReadToken = '<SAS token here>'
$resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')"
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$body = $entity | ConvertTo-Json
$item = Invoke-RestMethod -Method PUT -Uri $tableUri -Headers $headers -Body $body -ContentType application/json
}
$mBody = #{
PartitionKey = "MPS02000"
RowKey = "2019-000101"
appUpdateMode = "1"
m_CustID = "000000001"
}
addUpdateEntity -TableName "atdMachines" -PartitionKey $mBody.PartitionKey -RowKey $mBody.RowKey -entity $mBody
Q1. Pull all properties of an entity that matches a customer ID
Answer: You can use $filter query expression. For example, I have 2 entities in my testTable:
I can get the entity whose Id equals to 00001 by making a request as following:
GET https://storagetest789.table.core.windows.net/testTable?{sastoken}&$filter=(Id eq '00001')
$storageAccount = "storagetest789"
$tableName = "testTable"
$sasReadToken = "?sv=2019-02-02&ss=t&sr***************D"
$filter = "`$filter=(Id eq '00001')"
$tableUri = "https://$storageAccount.table.core.windows.net/$tableName$sasReadToken&$filter"
$GMTTime = (Get-Date).ToUniversalTime().toString('R')
$header = #{
'x-ms-date' = $GMTTime;
Accept = 'application/json;odata=nometadata'
}
$finalResult = Invoke-WebRequest -Uri $tableUri -Headers $header -UseBasicParsing
$finalResult = $finalResult.Content | ConvertFrom-Json
$finalResult.value
Result:
Q2. Update the same entity in the table
Answer: Both Invoke-WebRequest and Invoke-RestMethod are suitable for making a HTTP request here. I find some mistakes in your scripts, here is the fixed one:
function addUpdateEntity ($tableName, $PartitionKey, $RowKey, $entity){
$storageAccount = "storagetest789"
$tableName = "testTable"
# Need write access
$sasWriteToken = "?sv=2019-02-02&ss=t&s*****************************D"
$resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')"
# should use $resource, not $tableNmae
$tableUri = "https://$storageAccount.table.core.windows.net/$resource$sasWriteToken"
# should be headers, because you use headers in Invoke-RestMethod
$headers = #{
Accept = 'application/json;odata=nometadata'
}
$body = $entity | ConvertTo-Json
$item = Invoke-RestMethod -Method PUT -Uri $tableUri -Headers $headers -Body $body -ContentType application/json
}
$mBody = #{
PartitionKey = "p1"
RowKey = "r1"
Id = "00001"
Value = "new value"
}
addUpdateEntity -TableName "atdMachines" -PartitionKey $mBody.PartitionKey -RowKey $mBody.RowKey -entity $mBody
Result:

Resources