Getting 'Bad Request' and 'Invalid query definition' when calling Azure REST API - azure

I'm trying to call an Azure REST API endpoint to get information my daily usage cost.
I'm using Powershell 7.2 and here's my code:
$uri = 'https://management.azure.com/subscriptions/{subscription id}/providers/Microsoft.CostManagement/query?api-version=2021-10-01'
$token = '{generated bearer token string}'
$securetoken = ConvertTo-SecureString $token -AsPlainText -Force
$body = #{
type = 'Usage'
timeframe = 'MonthToDate'
dataset = #{
granularity = 'Daily'
aggregation = #{
totalCost = #{
name = 'PreTaxCost'
function = 'Sum'
}
}
grouping = #(
#{
type = 'Dimension'
name = 'ServiceName'
}
)
}
}
$costresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication Bearer -Token $securetoken -Body $body
Write-Host $costresponse
Here's the request body example from the Microsoft documentation I'm trying to emulate:
{
"type": "Usage",
"timeframe": "TheLastMonth",
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"grouping": [
{
"type": "Dimension",
"name": "ResourceGroup"
}
]
}
}
When I run the code I get this error message:
Line |
27 | … tresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| {"error":{"code":"BadRequest","message":"Invalid query definition: Missing dataset granularity; valid
| values: 'Daily'.\r\n\r\n (Request ID: c6ead005-85b3-4ebe-9b46-........)"}}
I think the error has to do with the body syntax but I can't figure out what is the issue. I'm following this Microsoft documentation: https://learn.microsoft.com/en-us/rest/api/cost-management/query/usage
EDIT:
I tried converting the body to JSON and adding a depth parameter like this:
$costresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication Bearer -Token $securetoken -Body ($body|ConvertTo-Json -Depth 5)
And now I get a slightly different error message:
Line |
26 | … tresponse = Invoke-WebRequest -Uri $uri -Method Post -Authentication …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| {"error":{"code":"BadRequest","message":"Invalid query definition, Dataset is invalid or not supplied.
| (Request ID: cf6a4b8f-88e8-4037-aa33-904......)"}}

I needed to add -ContentType 'application/json' to my Invoke-WebRequest function to get it to work. Thanks to #bluuf for the suggestion.

Related

How to get all key secrets after rest api collect all secrets in Azure Key Vault

Have a nice day everyone!
I have a VMware Windows Which has permission in key vault and I want to collect all key secrets but the code below when it finished just has ID + Attributes not consist value of Key secrets. Anyone can help me finish the code below to get all key secrets.
Many thanks for your help!
$RresourceUrl = 'dddd.vault.azure.net'
# Compose REST request.
$response = Invoke-WebRequest -Uri 'http://169.254.111.211/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers #{Metadata="true"}
$OAuth = $response.Content | ConvertFrom-Json
# 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://$RresourceUrl/secrets?api-version=2016-10-01"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-WebRequest -Uri $Uri -Method GET -Headers $HeaderParams | ConvertFrom-Json
$Results.nextLink
if ($Results.value) {
$QueryResults += $Results.value
}
else {
$QueryResults += $Results
}
$Uri = $Results.nextLink
} until (!($Uri))
# Return the result.
$QueryResults | Export-Csv -NoTypeInformatio *\Documents\Tesst.csv
}
else {
Write-Error "No Access Token"
}
This is my final code maybe isn't optimized but it worked.
$RresourceUrl = 'devakv01.vault.azure.net'
# Compose REST request.
$response = Invoke-WebRequest -Uri 'http://169.254.111.211/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net' -Method GET -Headers #{Metadata="true"}
$OAuth = $response.Content | ConvertFrom-Json
# 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://$RresourceUrl/secrets?api-version=2016-10-01"
# Invoke REST method and fetch data until there are no pages left.
do {
$Results = Invoke-WebRequest -Uri $Uri -Method GET -Headers $HeaderParams | ConvertFrom-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"
}
# get Key after to have secrets name
$output = ForEach ($nameSecret in $QueryResults.id)
{
$Resultskey = Invoke-WebRequest -Uri $($nameSecret+'?api-version=2016-10-01') -Method GET -Headers $HeaderParams | ConvertFrom-Json
$Resultskey
}
$output | Export-Csv -NoTypeInformatio *\$RresourceUrl.csv

Azure Devops REST API SendMail

I'm trying to send mail after the successful stage on my release definition.
Following the docs
OAuth box is checked in my stage
Project Collection Service Account is added to Build Administrators and Release Administrators.
But the response from REST API is "Azure DevOps Login Page"
Here is my script:
$OrganizationName = "myorg"
$ProjectName = "myproj"
$sendmailto = "example#mail.com"
$mysubject = "Test Mail Subjcet"
$mailbody = "Mail body to test it works with azure rest api"
$PAT="MYPAT"
$Token = "$(System.AccessToken)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$Token"))
$HeaderMail = #{
Authorization = "Bearer $encodedCreds"
}
##send mail
$urimail = "https://${OrganizationName}.vsrm.visualstudio.com/${ProjectName}/_apis/Release/sendmail/$($env:RELEASE_RELEASEID)?api-version=3.2-preview.1"
$requestBody =
#"
{
"senderType":1,
"to":{"tfsIds":[$sendmailto]},
"body":"${mailbody}",
"subject":"${mysubject}"
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}
Tested with:
Tried with 3.2 version and 7.1
PAT Token and authorization to Basic return 400 with Bearer return 401.
Switch $(System.AccessToken) to $($env:System_AccessToken) trygin to convert to base64 and without.
What I'm missing?
Response from
ConsoleLog
It's caused by the $requestBody. The request body requires valid Azure DevOps users referenced by their tfsIds.
Below PS script works for me, if your are running it in pipeline, then please use the $(System.AccessToken) instead of $PAT.
Running locally and authenticate with PAT:
$OrganizationName = "organization"
$ProjectName = "Project"
$sendmailto = "xxx#microsoft.com"
$mysubject = "Test Mail Subjcet"
$PAT="xxx"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$PAT"))
$HeaderMail = #{
Authorization = "Basic $encodedCreds"
}
#Get the tfsid
$userentitlementurl = "https://vsaex.dev.azure.com/${OrganizationName}/_apis/userentitlements?api-version=7.1-preview.1"
$response = Invoke-RestMethod -Uri $userentitlementurl -Method Get -Headers $HeaderMail
#Filter by sendmailto
$tfsid = ($response.value| where {$_.user.mailAddress -eq $sendmailto}).id
Write-Host $tfsid
##send mail
$urimail = "https://vsrm.dev.azure.com/${OrganizationName}/${ProjectName}/_apis/Release/sendmail/168?api-version=7.1-preview.1"
$requestBody =
#"
{
"senderType": 1,
"to": {
"tfsIds": [
"$tfsid"
],
"emailAddresses": []
},
"subject": "$mysubject",
"sections": [
5,
0,
1,
2,
4
]
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}
Running in release pipeline and authenticate with $(System.AccessToken): (Please note that, because this script is being run during the release, the summary email will show the environment as IN PROGRESS even if it is run as the last step in the Release.)
$OrganizationName = "organization"
$ProjectName = "project"
$sendmailto = "xxx#microsoft.com"
$mysubject = "Test Mail Subjcet"
$HeaderMail = #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
#Get the tfsid
$userentitlementurl = "https://vsaex.dev.azure.com/${OrganizationName}/_apis/userentitlements?api-version=7.1-preview.1"
$response = Invoke-RestMethod -Uri $userentitlementurl -Method Get -Headers $HeaderMail
#Filter by sendmailto
$tfsid = ($response.value| where {$_.user.mailAddress -eq $sendmailto}).id
Write-Host $tfsid
##send mail
$urimail = "$env:SYSTEM_TEAMFOUNDATIONSERVERURI$env:SYSTEM_TEAMPROJECT/_apis/Release/sendmail/$($env:RELEASE_RELEASEID)?api-version=7.1-preview.1"
$requestBody =
#"
{
"senderType": 1,
"to": {
"tfsIds": [
"$tfsid"
],
"emailAddresses": []
},
"subject": "$mysubject",
"sections": [
5,
0,
1,
2,
4
]
}
"#
Try {
Invoke-RestMethod -Uri $urimail -Body $requestBody -Method POST -ContentType "application/json" -Headers $HeaderMail
}
Catch {
$_.Exception
}

What causes "invalid query definition, dataset is invalid or not supplied" querying the Azure REST API with powershell?

I've tried multiple ways to get cost management information from the Azure REST API using Powershell. I'm copy/pasting the data for my $body variable directly from their documentation. I get this error with every example they've posted.
Below is my original attempt.
I've tried to pipe $body to convertto-json.
I've tried saving it as a .json file and then using get-content -raw to get it.
I've tried defining everything in a $params hash table and then using invoke-restmethod #params
No matter what I get the same error.
invalid query definition, dataset is invalid or not supplied
$method = "post"
$URI = "https://management.azure.com//subscriptions/12345678-1234-1234-1234-1234556789123/resourceGroups/HubRG/providers/Microsoft.CostManagement/query?api-version=2019-11-01"
$headers = #{
"authorization" = "Bearer verylongstringofcharacters"
}
$body = #"
{
"type": "ActualCost",
"dataset": {
"granularity": "Daily",
"aggregation": {
"totalcost" : {
"name": "cost",
"function": "Sum"
}
},
"grouping": [
{
"type": "Dimension",
"name": "ResourceGroup"
}
]
}
}
"#
Invoke-RestMethod -Method $method -URI $URI -Headers $headers -Body $body
I had this same issue - drove me crazy for at least an hour. It turned out that even though I was using Invoke-RestMethod, I had to explicitly set 'Content-Type' = 'application/json' in the headers of the request. Without it: 'Dataset is invalid...', with it: success.
$headers = #{
"authorization" = "Bearer verylongstringofcharacters"
"Content-Type" = "application/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"
}

graph beta /groups/$id/channels Authorization has been denied for this request

i want to get all channels from an azure group.
Querying the groups works and return all azure groups. however, query the group channels result in an error
Authorization has been denied for this request
Code:
$appID = "f9....22"
$appSecret="FH..8="
$tokenAuthURI = "https://login.microsoftonline.com/c6...59/oauth2/token"
$requestBody = "grant_type=client_credentials" +
"&client_id=$appID" +
"&resource=https://graph.microsoft.com" +
"&client_secret=$appSecret"
$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenAuthURI -body $requestBody -ContentType "application/x-www-form-urlencoded"
$accessToken = $tokenResponse.access_token
$groupsListURI = "https://graph.microsoft.com/beta/groups?`$filter=groupTypes/any(c:c+eq+`'Unified`')"
$graphResponse = Invoke-RestMethod -Method Get -Uri $groupsListURI -Headers #{"Authorization"="Bearer $accessToken"}
$TeamsList = #()
foreach ($group in $graphResponse.value)
{
if($group.groupTypes -eq "Unified") {
$id= $group.id
Try
{
$url = "https://graph.microsoft.com/beta/groups/$id/channels"
$team = Invoke-RestMethod -Method Get -Uri $url -Headers #{"Authorization"="Bearer $accessToken"}
"Channel count for " + $group.displayName + " is " + $team.value.id.count
}
Catch
{
$result = $_.Exception.Response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($result)
$reader.BaseStream.Position = 0
$reader.DiscardBufferedData()
$reader.ReadToEnd()
$team = $null
}
}
}
the catch result is ""
{
"error": {
"code": "",
"message": "Authorization has been denied for this request.",
"innerError": {
"request-id": "7c...89",
"date": "2017-12-14T14:19:39"
}
}
}
thx
UPDATE
also tried adding either $scope or $scope2. same result
$scope = "https://graph.microsoft.com/Group.ReadWrite.All https://graph.microsoft.com/Group.Read.All https://graph.microsoft.com/User.ReadBasic.All https://graph.microsoft.com/Directory.AccessAsUser.All https://graph.microsoft.com/Directory.Read.All"
$scope2 = "Group.ReadWrite.All Group.Read.All Directory.Read.All Directory.AccessAsUser.All User.ReadBasic.All"
$requestBody = "grant_type=client_credentials" +
"&client_id=$appID" +
"&resource=https://graph.microsoft.com" +
"&client_secret=$appSecret" +
"&scope=$scope2"

Resources