Checking if a Rest Call succeeds in PowerShell - rest

I'm currently writing a PowerShell script that will remove all users from a tool via Rest API. The current script works as intended but is lacking in error handling. The only thing I want to check for is if the Rest Call is successfully completed. After googling, I couldn't really find much or maybe I'm searching the wrong terms. Any thoughts or direction is much appreciated!

Here's an example of code I've used successfully (assuming you've defined Uri, Headers, and Body if required - also note the Method here is set to Post):
try
{
Write-Verbose "Calling $Uri"
Invoke-RestMethod -Uri $Uri -Method Post -Headers $Headers -Body $Json -ContentType 'application/json' -ErrorAction Stop
}
catch
{
throw $_
}

Related

Different results from Invoke-WebRequest and Invoke-RestMethod

I am trying to call Azure Rest API and get Schedules of DevTestLabs. I tried Invoke-RestMethod, but it doesn't give value on "dailyRecurrence" key. But Invoke-WebRequest does.
What would be the reason for that?
URL
$url = "https://management.azure.com/subscriptions/{subscriptionID}/resourceGroups/{resourseGroup}/providers/Microsoft.DevTestLab/labs/{LabName}/schedules/LabVmsShutdown?api-version=2018-10-15-preview"
URL with $expand
$url = "https://management.azure.com/subscriptions/{subscriptionID}/resourceGroups/{resourseGroup}/providers/Microsoft.DevTestLab/labs/{LabName}/schedules/LabVmsShutdown?$expand=properties(dailyRecurrence)&api-version=2018-10-15-preview"
Calling Invoke-RestMethod
$output = Invoke-RestMethod -Uri $url -Method "GET" -ContentType "application/json" -Headers $authHeaders
properties : #{status=Enabled; taskType=LabVmsShutdownTask; dailyRecurrence=; timeZoneId=AUS Eastern Standard Time;
notificationSettings=; createdDate=26/03/2019 4:38:18 PM; provisioningState=Succeeded;
uniqueIdentifier=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
Calling Invoke-WebRequest
$output = Invoke-WebRequest -Uri $url -Method "GET" -Headers $authHeaders
Content : {"properties":{"status":"Enabled","taskType":"LabVmsShutdownTask","dailyRecurrence":{"time":"1900"}
,"timeZoneId":"AUS Eastern Standard Time","notificationSettings":{"status":"Disabled","timeInMinute
s":30},"createdDate":"2019-03-26T03:38:18.0726376+00:00","provisioningState":"Succeeded","uniqueIde
ntifier":"XXXXXXXXXXXXXXXXXXXXXXXXX"},"id":"/subscriptions/XXXXXXXXXXXXXXXXXXX/resourcegroups/XXXXXXXXXXXXX/providers/microsoft.devtestlab/labs/XXXXXXXX/schedules/labvmsshutdown","name":"LabVmsShutdown","type":"microsoft.devtestlab/labs/schedules","location":"australiasoutheast"}
The problem:
is merely a display problem,
albeit one that exposes a long-standing bug, still present as of PowerShell Core 7.2.0-rc.1, namely that [pscustomobject] instances mistakenly stringify to the empty string - see GitHub issue #6163.
In short:
The data is there in the Invoke-RestMethod output, it just appears to be missing - see the next section.
Use $output.properties.dailyRecurrence to see it or, to more helpfully visualize the output, use it $output | ConvertTo-Json to re-convert it to (prettified) JSON.
Note: Situationally, you may have to add a -Depth argument to fully represent object graphs with a depth greater than 2 - see this post.
Invoke-RestMethod - unlike Invoke-WebRequest - has deserialization built in: with a JSON response, it automatically parses the JSON text returned into a [pscustomobject] graph as if ConvertFrom-Json had been applied to it.
You're seeing the default formatting of the resulting object graph, which isn't really suited to visualizing nested [pscustomobject] instances, which are represented by their .ToString() values - and therefore - due to the bug - may appear to have no value, even when they do.
By contrast, since the output from Invoke-WebRequest reports the JSON text as-is in the .Content property of its output objects, the problem doesn't surface there.
A simple demonstration of the bug:
[pscustomobject] #{
nested =
[pscustomobject] #{
deep =
[pscustomobject] #{
deepest = 'down'
}
}
}
As of PowerShell Core 7.2.0-rc.1, this yields the following, mistakenly suggesting that .nested.deep has no value, even though it clearly does:
nested
------
#{deep=}

Invoke-WebRequest with a random token + forms unavailable

Website I am attempting to do this on: https://www.netvendor.net/login
I am trying to login to the website automatically and kick off a script to create users in bulk for us.
If you run an Invoke-WebRequest on this page, it returns information, but nothing about the forms. They are simply not displayed. However, if you view the page source or inspect element, there are clearly forms on the page and they are not composed of JS or anything else that would mess it up. How can I get PowerShell to recognize these fields? I am using the following command:
Invoke-WebRequest -Uri "www.netvendor.net/login" -Method GET -UseBasicParsing
Because of the issue above, I decided I would just POST the information I needed by examining the request. The request requires three things:
email
password
_token
Unfortunately, the token is randomly generated each time a browser session is initiated. If you view source on the page and search for "_token", you will get the parameter that is needed. It doesn't seem like there is any way to retrieve this from the page? I am a bit lost as to what I can do at this point, especially since there is no API or anything else for me to work with.
For all interested, here is the final working script:
$nvlogin = Invoke-WebRequest "https://www.netvendor.net/login" -SessionVariable "netvendor"
$nvtoken = $nvlogin.InputFields.Where({ $_.Name -eq "_token" })[0].Value
$nvbody = #{
"_token" = $nvtoken
"email" = "your.name#website.com"
"password" = 'credentials'
}
Invoke-WebRequest -Uri "https://www.netvendor.net/login" -WebSession $netvendor -Method 'POST' -Body $nvbody

Error while updating VSTS release definition from powershell

I use the APIs listed in the VSTS API documentation here. On modifying a variable and saving the definition the error I get from the server is VS402982: Retention policy is not set for the environment 'environmentName'.
The portion of the PS script that performs the update is -
$c = Invoke-WebRequest 'https://accountname.vsrm.visualstudio.com/projectname/_apis/release/definitions/definitionId' -Method Get -Headers #{Authorization = 'Bearer ' + $authtoken}
$jsonObj = $c | ConvertFrom-Json
$url3 = "https://accountname.vsrm.visualstudio.com/projectname/_apis/release/definitions/definitionId?api-version=4.1-preview.3";
$contentType3 = "application/json"
$headers3 = #{
Authorization = 'Bearer ' + $authtoken
};
$d = $jsonObj | ConvertTo-Json;
Invoke-RestMethod -Method PUT -Uri $url3 -ContentType $contentType3 -Headers $headers3 -Body $d;
What could be wrong here?
This problem has been reported a couple of times in different forms, and is mostly related to a small problem with the powershell code in the question.
If you see error like the one mentioned in the question or this - VS402903: The specified value is not convertible to type ReleaseDefinition. Make sure it is convertible to type ReleaseDefinition and try again it means that there is a problem in the JSON object that you are posting to the server. Easiest problem will be to capture the request payload and analyse it for issues.
However, in the code mentioned in the question, the problem lies with powershell's ConvertTo-JSON method. Do note, the release definition has multiple layers of nested objects, definition -> environment -> steps/approvals etc, and ConvertTo-JSON by default goes only 2 levels deep to form a JSON object, which means you are missing some vital properties while calling the VSTS APIs. The fix would be to specify a large value for the -Depth parameter so that you do not miss any properties while calling the service.
eg. ConvertTo-Json -Depth 100
More details on the problem and how it's fixed in the script can be seen here.
Additionally to divyanshm's solution make sure encoding is correct:
$d = [Text.Encoding]::UTF8.GetBytes($d)

PowerShell and Google Calendar - 400 Error when creating event

so this'll be the first time posting on the Google Dev Forum, fingers crossed I can get this resolved as I am pulling my hair out!
I have the following script in PowerShell. I have sorted the OAuth2 and I am able to hit my Calendar API and see the hits in the dashboard. So, authentication is fine. I used the API Explorer to get a rough idea of what I would need to send in order to create a new Calendar event, but in doing so I get a 400 error.
Here's the section I am running:
$requestUri = "https://www.googleapis.com/calendar/v3/calendars/mysite#blah.com/events?"
$body=#{
end='dateTime:2017-06-07T08:30:00+01:00'
start='dateTime:2017-06-07T08:00:00+01:00'
}
$json = $body | ConvertTo-Json
$header=#{Authorization = "Bearer $($tokens.access_token)"}
Invoke-RestMethod -Headers $header -Uri $requestUri -Method POST -Body
$json -ContentType 'application\json' -TransferEncoding gzip
Here is the error I receive:
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
$json is formed like this:
{
"end": "dateTime:2017-06-07T08:30:00+01:00",
"start": "dateTime:2017-06-07T08:00:00+01:00"
}
I can remove transferEncoding and I get the same error.
As always, any help would be great :)
thanks
Moving my comment to an answer, the -ContentType looks weird with a backslash in it, and maybe it should be a forward slash, e.g.
$json -ContentType 'application/json'

Determine folder IDs with Invoke-RestMethod on Office 365

When I provide this in PowerShell:
Invoke-RestMethod -Uri "https://outlook.office365.com/api/v1.0/users/andrew.stevens#mydomain.com/folders/"
-Credential $cred | foreach-object{$_.value |select DisplayName,ID}
I successfully determine Folder IDs, but not all folders are visible. How do I get a complete listing of folders (the one I particular want is recoverable items). I'm thinking once I get the ID I can see the messages the folder would contain?
Did you mean the Deleted Items folder?
If I understand correctly, it should be listed by the REST you calling. And we can use the well-known folder names: DeletedItems to get the messages. Here is an example for your reference:
Get: https://outlook.office.com/api/v2.0/me/MailFolders/DeletedItems/messages
Update
To use the Office 365 REST API, we need to use the bearer token which we need to register the app first. Below is an sample to get the access token via PowerShell for your reference(refer to Obtaining an Access Token):
#region Construct Azure Datamarket access_token
#Get ClientId and Client_Secret from https://datamarket.azure.com/developer/applications/
#Refer obtaining AccessToken (http://msdn.microsoft.com/en-us/library/hh454950.aspx)
$ClientID = '<Your Value Here From Registered Application>'
$client_Secret = ‘<Your Registered Application client_secret>'
# If ClientId or Client_Secret has special characters, UrlEncode before sending request
$clientIDEncoded = [System.Web.HttpUtility]::UrlEncode($ClientID)
$client_SecretEncoded = [System.Web.HttpUtility]::UrlEncode($client_Secret)
#Define uri for Azure Data Market
$Uri = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
#Define the body of the request
$Body = "grant_type=client_credentials&client_id=$clientIDEncoded&client_secret=$client_SecretEncoded&scope=http://api.microsofttranslator.com"
#Define the content type for the request
$ContentType = "application/x-www-form-urlencoded"
#Invoke REST method. This handles the deserialization of the JSON result. Less effort than invoke-webrequest
$admAuth=Invoke-RestMethod -Uri $Uri -Body $Body -ContentType $ContentType -Method Post
#Construct the header value with the access_token just recieved
$HeaderValue = "Bearer " + $admauth.access_token
#endregion
#region Construct and invoke REST request to Microsoft Translator Service
[string] $text = "Use pixels to express measurements for padding and margins.";
[string] $textEncoded = [System.Web.HttpUtility]::UrlEncode($text)
[string] $from = "en";
[string] $to = "de";
[string] $uri = "http://api.microsofttranslator.com/v2/Http.svc/Translate?text=" + $text + "&from=" + $from + "&to=" + $to;
$result = Invoke-RestMethod -Uri $uri -Headers #{Authorization = $HeaderValue}
#endregion
$result.string.'#text'
After we get the access token, we can use the sample below to make the REST call:
$uri ="https://outlook.office.com/api/v2.0/me/MailFolders/DeletedItems/messages"
$accessToken=''
Invoke-RestMethod -Uri $uri -Headers #{Authorization=("bearer {0}" -f $accessToken)}
Links below are also helpful for you learning Office 365 REST API:
Manually register your app with Azure AD so it can access Office 365 APIs
Office 365 APIs platform overview
Thanks for the comment, and yes DeletedItems would be the folder name (for deleted items) but I discovered this..
https://msdn.microsoft.com/en-us/library/office/dn424760(v=exchg.150).aspx.
And from this you can do this..
Invoke-RestMethod -Uri "https://outlook.office365.com/api/v1.0/users/andrew.stevens#anydomain.com/folders/recoverableitemsdeletions/messages?
" -Credential $cred | foreach-object{$.value |select #{n='Sender';e={$.sender.emailaddress.name}},Subject,DateTimeReceived}
So this gets messages in the recovered deleted items container.
Question now is how do I move them to inbox?
//A
OK.. thanks for the information regarding app registration and OAuth. But just to see the recovered deleted items the method using v1.0 does actually work..
What I now need to understand (apologies for my lack of knowledge on this) is how to move messages from the folder located (recovered deleted items) to say the Inbox (or perhaps another folder of choice). Since I was able to use V1.0 (with no requirement for app registration) can I use the same version to do this?
Your answer you provided for sending email worked perfectly and that didnt require any app registration.. so this would be a POST of somekind..
All I have to go on is
1. POST https://outlook.office.com/api/v1.0/me/messages/{message_id}/move
2. {
"DestinationId": "AAMkAGI2NGVhZTVlLTI1OGMtNDI4My1iZmE5LTA5OGJiZGEzMTc0YQAuAAAAAADUuTJK1K9aTpCdqXop_4NaAQCd9nJ-tVysQos2hTfspaWRAAAAAAEJAAA="
}
Which is a folder ID
//A