Powershell, Graph API and $select - powershell

I have an MS Graph API in PowerShell working for the most part.
I am using
$Uri = $null
$Uri = "https://graph.microsoft.com/v1.0/users?$select=displayName,givenName,postalCode"
$payload=$null
$payload = Invoke-RestMethod -uri $Uri -Headers $Header -Method Get -ContentType "application/json"
$payload.value
however, it is not changing the field selection. It keeps returning the default fields as demonstrated here
https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http
What could I possibly be doing wrong?
I am using application based authentication. The payload is being returned but it is not recognizing the $select statement.
No errors are being returned by the PowerShell
I run it in Graph Explorer it works fine.

The error is caused by the type of string declaration used for the Uri string. You are declaring the string like this:
$Uri = "https://graph.microsoft.com/v1.0/users?$select=displayName,givenName,postalCode"
This tells Powershell, that you want to evaluate the string. $ is Powershell's variable identifier. Undeclared variables are set automatically to an empty string, when evaluated in a string. Therefore the request executed against the Graph Api is:
https://graph.microsoft.com/v1.0/users?=displayName,givenName,postalCode
Your can check this yourself by writing the variable to the host:
Write-Host $Uri
If you execute this query with the Graph Explorer. It will return all users without an applied filter, which is the behaviour you have observed. You need to change the declaration to:
$Uri = 'https://graph.microsoft.com/v1.0/users?$select=displayName,givenName,postalCode'
Then, Powershell will not interpret $select as a variable and your request should work properly.

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=}

Powershell - Invoke-RestMethod with multiple headers

I am trying to use Invoke-Restmethod in Powershell to call an API (I'm fairly new to this). I can get it to POST and return the jwt access token. I can also use that token to return an id via GET, however I'm then having trouble with the next step of returning the next set of data. I can get it to work manually via curl. I believe the issue may be because multiple headers are required to return the tenant list and I'm unsure of the format to get this to work.
The curl script looks as follows, and works as expected:
curl -XGET -H "Authorization: Bearer <jwt access token>" -H "ID: <id>" https://theapiurl.com/.......
I've tried multiple ways to do this in powershell, most recently as below, but nothing I'm trying works. I've tried returning the individual $headers contents and building a string (i.e. $headers2 = $.headers.Item(Authorization) + ......) but that doesn't work either. To be honest, I've tried so many different things I've forgotten what I have and haven't tried
$headers = #{
'ID' = $id
'Authorization' = $auth_string
}
$response = Invoke-RestMethod -Method Get -Headers $headers -Uri $url
Please could you let me know the correct way to add multiple headers (which I think is the problem and what I'm getting wrong)?
In case it's useful to anyone else, another syntax for setting the parameters of this commandlet is as follows (real working example for uploading to the GitHub release repository). It's usful to set all the switches (without prepending a hyphen) in the parameters object like so:
$upload_params = #{
Uri = $upload_uri + "?name=$asset_name"
Method = 'POST'
Headers = #{
'Authorization' = "token $github_token"
'Accept' = 'application/vnd.github.everest-preview+json'
}
InFile = $asset
ContentType = 'application/octet-stream'
}
"Uploading $asset..."
$upload = Invoke-RestMethod #upload_params
"The server returned:"
echo $upload
The variable $upload contains the full object returned from the server (converted from json to a PowerShell object). So, for example, you can also get properties of this obect like so:
"Upload successfully posted as " + $upload.url
Thanks for all the responses - none of them were really the answer but they did give me the confidence I was doing it the right way.
I'd been using PS Write-Host to check the data returned - this was working for the tokens and ID's, but wasn't working for next step. I wasn't getting an error, just no data. (I did see the returned data when testing manually in a command prompt window).
As soon as I added an -OutFile to the PS and checked the file, I realised it was working all along and PS just wasn't showing me the results. 2 hours wasted, although I've learnt more as a result!

Azure DevOps Rest API in Powershell saying no parameters matching

I am starting to use PowerShell to call the Azure DevOps REST API. But it seems like when I try to add parameters it tell me:
A parameter cannot be found that matches parameter name
'repositoryId'
Here is what my call looks like in PowerShell. If I take out the parameter it works. What am I doing wrong?
Invoke-RestMethod -Uri 'https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.1' -repositoryId $repoId -Headers (my authentication) -Method Get
Per Microsoft's documentation this should work.
https://learn.microsoft.com/en-us/rest/api/azure/devops/build/builds/list?view=azure-devops-rest-5.1
repositoryId should be url parameter as Booga Roo mentioned. The error indicated that Repository type is missing.
You should add another parameter to your uri repositoryType={repositoryType}.So the uri should be like below.
Please check here for all repositoryTypes
https://dev.azure.com/{Organization}/{Project}/_apis/build/builds?repositoryId={id}&repositoryType=TfsGit&api-version=5.1
Addition:
You can get your repositoryId from URL of Repositories page under Repos in the Project Settings. Check below screentshot.
The Invoke-RestMethod cmdlet does not have a -repositoryId parameter. The phrasing and examples on the help page are for "URI Parameters" instead of PowerShell parameters. It means you need to build it into -Uri value instead of trying to use it directly.
I suggest using this:
Invoke-RestMethod -Uri "https://dev.azure.com/{organization}/{project}/_apis/build/builds?repositoryId={$repoId}&api-version=5.1" -Headers (my authentication) -Method Get
Side note: There are double quotes around this example URI. This is so the variable expansion for $repoId will occur and be properly interpreted as part of the URI. Using single quotes as in the original example will prevent this and treat it as a literal string value and won't perform any subsitutions.

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)