azure billing REST API - azure-billing-api

I'm trying to get Azure Billing data of my subscription data by using Powershell.
mainly checked the Doc from MSDN
https://learn.microsoft.com/ja-jp/rest/api/consumption/usagedetails/list
and a sample as below.
https://www.cloudnative.at/2017/12/22/generate-an-azure-consumption-report-with-the-consumption-rest-api-and-powershell/
$loginUri = "https://login.microsoft.com"
$body =@{
client_id = XXXX
client_secrect = XXXXXXXX
resource = "https://management.core.windows.net"
grant_type = "client_credentials"
}
$oauth = Invoke-RestMethod -Method Post -Uri $loginUrl/$TenantID/oauth2/token?api-version=1.0 -Body $body
# SubscriptionID and Billing Period
$SubscriptionId = '<Your subscription GUID here>'
$billingperiod = '202006-1'
#Create the REST-URL
$usageURL = "https://management.azure.com/subscriptions/$subscriptionid/providers/Microsoft.Billing/billingPeriods/$billingperiod/providers/Microsoft.Consumption/usageDetails?api-version=2017-11-30"
After I got the authentication token successfully, got error when running request uri like
“AuthenticationFailed”, the client 'XXXXXX' with object id 'XXXX' does not have authorization to perform action 'Microsoft.Consumption/usageDetial/read' over scope '/subscriptions/XXXX' or the scope is invalid. If access was recently granted, please refresh your credential.
Might because I didn't use APPID and genarated APPkey to get credentials, instead using client_secret of application as I get token in Graph API?

If you want to access Azure billing api with Azure AD application, we need to assign Azure RABC role(Billing Reader, Reader, Owner, or Contributor role) to the AD application.For more details, please refer to the document
For example(I assign Contributor role)
Step 1: login to your azure portal
Step 2: find Subscriptions in left side menu bar and click.
step 3: Click on Access Control IAM and then click on Add.
Step 4: In Add Permission window, select contributor for role. In select input box, type the app name you created in Azure AD (Created in Azure Active Directory)and select it. In my case I created Azure Resource Management.
Step 5:After you have given successful permission, click on Refresh in your subscription window and you will see your app showing in the list. See below example.
step6: Powershell script
$tenantId="76a1f773...b-86b9-d1ced3e15cda"
$clientId="0159ec7d-f...-a680-c4d40ab7a36c"
$clientSecret="o4eq4jj...I26uz26W~"
$secSecret = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$pscredential = New-Object System.Management.Automation.PSCredential ($clientId, $secSecret)
Connect-AzAccount -ServicePrincipal -Credential $pscredential -Tenant $tenantId
$dexResourceUrl="https://management.azure.com/"
$context = Get-AzContext
$token = [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, $dexResourceUrl).AccessToken
$SubscriptionId = '3465e081-85b6-4b54-a3e1-15675acb615f'
$billingperiod = '202010-1'
#Create the REST-URL
$usageURL ="https://management.azure.com/subscriptions/$subscriptionid/providers/Microsoft.Billing/billingPeriods/$billingperiod/providers/Microsoft.Consumption/usageDetails?api-version=2017-11-30"
$header = #{
'Authorization' = "Bearer $($token)"
"Content-Type" = "application/json"
}
$UsageData = Invoke-RestMethod `
-Method Get `
-Uri $usageURL `
-ContentType application/json `
-Headers $header
ConvertTo-Json $UsageData

Having followed the instructions here I get the following error when making the actual API request:
Invoke-RestMethod : {"error":{"code":"500","message":"An error occurred during processing this request. Use this request id
'17f6fdea-xxxx-xxxx-xxxx-a8c314d770ee' for follow-up."}}
At C:\$Downloads\billingapitest.ps1:45 char:14
+ $UsageData = Invoke-RestMethod `
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Any ideas how to resolve?
Thanks
Darren
============================================
I filed a ticket with MS support. Just prior to the session that had been scheduled to demonstrate the issue, I tested the Powershell script one more time and found that it was now working!

Related

ConverFrom-JSON Error: Invalid JSON Primitive || PowerShell PoweBi Embeded Token Generator Code

ConverFrom-JSON : Invalid JSON Primitive error, primitive is not being recognized by PowerShell for some reason.
I need to generate PowerBi Embedded Tokens for my PowerBi reports. I can log in to my Microsoft account with out no problem and can invoke the report as well. At the end code supposed to return a embedded token to me, but I encounter with error :
ConvertFrom-Json : Invalid JSON primitive: .
At line:13 char:21
+ $json = $response | ConvertFrom-Json
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand
I have already tried the Out-String and Raw with Get-Content method, they did not work either.
CODE:
//Sign in with a user that has admin rights to App Workspace
Login-PowerBI
//Regular Report
$url = "https://app.powerbi.com/reportEmbed?reportId=5515f33b-c114-41c9-a925-d1f85c323dab&groupId=e53e4fcd-16f8-46ef-8740-8e7167562ceb&autoAuth=true&ctid=c760270c-f3da-4cfa-9737-03808ef5579f/GenerateToken"
$body = "{ 'accessLevel': 'View' }"
$response = Invoke-PowerBIRestMethod -Url $url -Body $body -Method Post
$response
$json = $response | ConvertFrom-Json
$json.token
It should return a huge paragraph of gibberish code, which will be my report's embedded token.
$url is incorrect - need to call api.powerbi.com
https://api.powerbi.com/v1.0/myorg/groups/{GroupID}/reports/{ReportID}/GenerateToken
try
$url = "https://api.powerbi.com/v1.0/myorg/groups/e53e4fcd-16f8-46ef-8740-8e7167562ceb/reports/5515f33b-c114-41c9-a925-d1f85c323dab/GenerateToken"

Azure Runbook Webhook with parameters from httpClient

looked at a few examples of how to execute a webhook with parameters but can't seem to make the connection on what I am missing. Any advice on what I am doing wrong would be appreciated.
please consider:
my Powershell runbook
[CmdletBinding()]
Param([object]$WebhookData) #this parameter name needs to be called
WebHookData otherwise the webhook does not work as expected.
$VerbosePreference = 'continue'
Write-Output "hello"
"in the inline"
if($WebhookData -ne $null)
{
"using webhookdata"
$WebhookName = $WebhookData.WebhookName
$WebhookBody = $WebhookData.RequestBody
$webhookBodyObject = $WebhookBody | ConvertFrom-JSON
line 15 'The parameter created was ' $webhookBodyObject.strYear
My httpclient post request looks like this (warning..its vb)
dim WebHookData as new StringContent("{'strYear'='2018'}",Encoding.UTF8,"application/json")
Dim resp as Task(Of HttpResponseMessage)
resp = _client.PostAsync(webhook,WebHookData)
status = resp.Result.Content.ReadAsStringAsync().Result
if(status.Contains("JobId"))
status = "Scheduled!"
End If
My Webhook data is being posted to my webhook as this.
{"WebhookName":"myimportjob","RequestBody":"{'strYear'='2018'}","RequestHeader":{"Connection":"Keep-Alive","Expect":"100-continue","Host":"xxx.azure-automation.net","x-ms-request-id":"xxx"}}
I am getting this error
At line:15 char:42
+ 'The parameter created was ' $webhookBodyObject.strYear
+ ~~~~~~~~~~~~~~~~~~ Unexpected token '$webhookBodyObject' in expression or
statement.
I discovered the devil is in the details.
Firstly I had
$webhookBodyObject = $WebhookBody | ConvertFrom-JSON
which is not the same as
$webhookBodyObject = $WebhookBody | ConvertFrom-Json <---this is the correct syntax
The other was that the json I was sending had a single quote like this '{"key":"value"}'
For some reason, even though it passed regular powershell, the runbook didn't like it. It wants it's Json like this {"key":"value"}. I never tested complex objects so I can't speak to that.

smartsheet api update rows error 1004. you are not authorized

I am trying to use powershell and the API to update a cell, but I get an error: 1004 You are not authorized to perform this action. I've elided the IDs . . .
I have a 30 day trial. Do I need to buy in to some level of support before I can use the API to do updates or what? The sheet is on my account and I am the owner. Is Write Sheets role not included in Owner?
I am using powershell 5.1 to make the http request – here is my script. It first gets the correct sheet ID, then puts the rows and columns I am interested in into hashes, and then tries to update the sheet, but although I am the owner, I am unauthorized.
# access account of jjj.mmm#ddd.com
$apiKey = "3ccfgk..."
$url = "https://api.smartsheet.com/2.0/sheets/"
$get_headers = #{"Authorization" = "Bearer " + $apiKey}
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json" }
# get all the sheets
$response = Invoke-RestMethod -Uri $url -Headers $get_headers
$rd = $response.data
$json = ConvertTo-Json $rd
# find the Forecast sheet
$sheet_id = $rd | Where {$_.name -eq "Forecast Intermediary Sheet"} | Select -ExpandProperty id
"Sheet ID: $sheet_id"
#get the Forecast sheet
$surl = "$url$sheet_id"
$surl
$response = Invoke-RestMethod -Uri $surl -Headers $get_headers
$response | Format-List
# iterate over the rows and get the active ones
$active_rows = #{}
foreach ($row in $response.rows) {
if ($row.rowNumber -ge 14) {
if ($row.cells[2].value -Match "Active") {
$active_rows.Add($row.cells[2].value, $row.id)
}
}
}
# get the col_ids by date
$date_cols = #{}
foreach ($c in $response.columns) {
if ($c.title -as [datetime]) {
$d = $c.title -replace "/20", "/"
$ds = [datetime]::parseexact($d, 'mm/dd/yy', $null)
$date_cols.Add($ds.Tostring("yyyy-mm-dd"), $c.id)
}
}
"UPDATE:"
$rowid = $active_rows["SSL PS Active"]
$colid = $date_cols["2017-11-01"]
"row: $rowid, col: $colid"
$json = '[{ "id": "'+$rowid+'", "cells": [{"columnId": "'+$colid+'","value": "23"} }]'
"JSON: $json"
$purl = "$surl/rows"
"PUT URL: $purl"
$r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers -Body $json
$r
Below is the output of the script. The error seems to indicate it is syntactically correct, but OWNER is insufficient permissions.
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
UPDATE:
row: 5668..., col: 5118...
JSON: [{ "id": "5668...", "cells": [{"columnId": "5118...","value": "23"} }]
PUT URL: https://api.smartsheet.com/2.0/sheets/5724.../rows
Invoke-RestMethod : {
"errorCode" : 1004,
"message" : "You are not authorized to perform this action.",
"refId" : "14a7o8lu9sfyj"
}
At \\winfiles\jmoore\powershell\ss_api.ps1:58 char:6
+ $r = Invoke-RestMethod -Method "PUT" -uri $purl -Headers $put_headers ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodComand
Here is the meta data of my sheet
Sheet ID: 5724...
https://api.smartsheet.com/2.0/sheets/5724....
id : 5724...
name : Forecast Intermediary Sheet
version : 2
totalRowCount : 37
accessLevel : OWNER
effectiveAttachmentOptions : {GOOGLE_DRIVE, DROPBOX, ONEDRIVE, EGNYTE...}
ganttEnabled : False
dependenciesEnabled : False
resourceManagementEnabled : False
cellImageUploadEnabled : True
userSettings : #{criticalPathEnabled=False; displaySummaryTasks=True}
permalink : https://app.smartsheet.com/b/home?lx=LCTEj6F0xWNKWWxFUuLH0w
createdAt : 2017-11-22T22:34:51Z
modifiedAt : 2017-11-22T22:34:51Z
columns : {#{id=6985...; index=0; title=Column1; type=TEXT_NUMBER; primary=True;
validation=False; width=64}, #{id=1356...; index=1; title=Column2;
type=PICKLIST; options=System.Object[]; validation=False; width=64},
#{id=5859...; index=2; title=Column3; type=TEXT_NUMBER; validation=False;
....
You should be able to make API calls using an API Access Token that's owned by a trial Smartsheet account, and you can most certainly update a sheet that you own using an API Access Token you own.
I'd suspect that the 1004 error response is being caused by the fact that the contents of $put_headers isn't formatted properly. i.e., Smartsheet isn't able to accurately parse the headers you're attempting to specify in $put_headers in order to identify the Authorization header and read its value. When Smartsheet doesn't see the Authorization header in an inbound API request, it'll respond with the 1004 You are not authorized to perform this action. error response.
To troubleshoot this issue, I'd suggest that you use a tool like Fiddler to examine the outbound "Update Rows" request, paying special attention to what headers are present in the request. Then, if you discover that the Authorization header is absent from the request, you'll need to figure out how to specify multiple request headers in PowerShell, and update your code accordingly re how you're setting the value of $put_headers.
Update (adding PowerShell code):
I'm no PowerShell expert, but you might try replacing this line:
$put_headers = #{"Authorization" = "Bearer " + $apiKey, "Content-Type: application/json"}
With these lines instead:
$put_headers = #{}
$put_headers.Add("Authorization", "Bearer " + $apiKey)
$put_headers.Add("Content-Type", "application/json")
Thanks #kim-brandl
Or if you want to build the headers in a single command, you need to use syntax like this:
$put_headers = #{"Authorization" = "Bearer " + $apiKey; "Content-Type" = "application/json" }
Note also that your json payload has a few problems including:
Unmatched brackets
Extra square brackets on the outside (the body is an object, not an array)
Column and row ids must be numbers, not strings
I suggest you test the payload in a tool like postman first.

Jira API POST and Invoke-RestMethod

Using Jira API 2 and PowerShell 5 Invoke-RestMethod I can successfully execute GET, but I keep getting a (400) Bad Request when attempting POST method to create an issue in my project.
$user = [System.Text.Encoding]::UTF8.GetBytes("me:mypassword")
$headers = #{Authorization = "Basic " + [System.Convert]::ToBase64String($user)}
$data = Get-Content D:\scripts\powershell\issue.txt
Invoke-RestMethod -Uri "https://agile.mycompany.com/rest/api/2/issue/" -Method POST -Headers $headers -ContentType "application/json" -Body $data
$data variable is well-formed JSON for Jira:
{
"fields":
{
"project":{"Key": "ITS"},
"summary":"Rest Test 1",
"issuetype":{"name": "Task"},
"assignee":{"key": "myusername"},
"priority":{"id": "3"},
"description":
"||Host Name||IP Address||Comments||
|some-pc|192.168.1.1| |",
"duedate": "2016-09-11"
}
}
I am the project owner, so this isn't a permissions issue.
Get-content is tricky, because it will actually result in a array of strings, where each line in your text file is an object in that array. The best way to get around that is probably using .Net's file read methods instead:
$data = [System.IO.File]::ReadAllText("D:\scripts\powershell\issue.txt")
btw, you can use a regular ps credential object instead of manually building your request header.
On a side note, it's always a good idea to test your api out using a tool such as postman. That will let you verify that you're posting valid json while not having to worry about your code doing strange things.
Problem solved. The issue was that I was CAPITALIZING the first letter of the field names. Apparently, Jira is very sensitive to CASE. Trondh - thank you for the recommendation to use postman. The errors that were getting generated by postman from the failed API calls were very concise.

How to correctly pass variables & source version to API 2.0 VNext Build in TFS 2015

I'm having difficulty finding how the correct way to pass defined variables and build definition arguments to the new API 2.0 build engine with TFS 2015. I'm using TFS 2015 Update 3 on-premise .
I've triggered a POST with powershell that looks like this:
$Build_Definition_ID = 1234
$TFSInstanceURL = 'http://tfsservername:port/tfs'
$ProjectCollection = 'CollectionName'
$TeamProject = 'ProjectName'
$Changeset = "12345"
$UserName = "$env:USERDOMAIN\$env:USERNAME"
$UserNamePartial = $env:USERNAME
$body = #"
{
"definition": {
"id": "$Build_Definition_ID"
}
}
"#
$baseUri = $TFSInstanceURL+"/"+$ProjectCollection+"/"+$TeamProject+"/_apis/build"
$postUri = $baseUri+"/builds?api-version=2.0"
##Create a new PSCredential based on username/password
$User = 'foo\bar'
$Password = 'examplepass'
$securePassword = $Password | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($User, $securePassword)
### Queue a build ###
##Call the REST API for TFS that does a POST request to queue a build with the body of the request to be the build definition
$buildResponse = Invoke-RestMethod -Method POST -Credential $credential -ContentType application/json -Uri $postUri -Body $body
#Write-Host (ConvertTo-Json $buildResponse)
#ConvertTo-Json $buildResponse | out-file -FilePath $Changeset-ResponseJson.json -Force
The powershell script is successfully launching the definition. However, I'm still not successfully:
- Passing in the specific source version I want to run against (example C12345)
- Passing in the custom variable values
Additionally:
If you know of the proper way to pass in the arguments such as the folder to map from source (to allow dynamically choosing different branches) then this would help.
Current resources I've evaluated:
Visual Studio Docs > Api > Build > Builds
Postman - GET - Definition Details - Reviewed response for possible correct structure to submit
The body part for the REST API should look like:
{
"definition": {
"id": 28
},
"sourceBranch": "$/xxxx/xxxx",
"SourceVersion": "Cxxxx",
}
Then you can specify the sourceBranch and SourceVersion.
===================================================================
An example:
$Build_Definition_ID = '28'
$TFSInstanceURL = 'http://tfsservername:port/tfs'
$ProjectCollection = 'DefaultCollection'
$TeamProject = 'TestCase'
$Changeset = "C139"
$sourceBranch = "$/TestCase/TestCaseProject-branch"
$body = #"
{
"definition": {
"id": "$Build_Definition_ID"
},
"sourceBranch": "$sourceBranch",
"SourceVersion": "$Changeset",
}
"#