I am trying to use the Microsoft Graph API with Application registration and permissions to modify Planner Tasks.
I have successfully registered my application and set permissions to (Directory.Read.All, Directory.ReadWrite.All, Group.Read.All, Group.ReadWrite.All, GroupMember.Read.All, GroupMember.ReadWrite.All, User.Read). but I can't pull the Plans for any group I always get the error:
> Invoke-RestMethod : { "error": {
> "code": "UnknownError",
> "message": "UserDeleted",
> "innerError": {
> "request-id": "043b140e-aa18-42aa-8672-7c164277553f",
> "date": "2020-05-16T22:47:10"
> } } } At C:\Users\jlamb\Scripts\Connect-MicrosoftGraph.ps1:36 char:17
> + ... $Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri ...
> + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod],
> WebException
> + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
I don't understand what this error means.
Here is my sample code:
$tenant = '67a80b53-1b4f-4278-b269-xxxxxxxxxxxx' #Directory ID
$client_id = 'da398f63-3b2b-4dc3-b594-54cbc0a2924f' #Application ID
$scope = 'https://graph.microsoft.com/.default'
$client_secret = 'xxxxx-xxxx.xxxxxxx.xxxxxxxxxxx.xxx' #PowerShellPOC, expires 5/16/2021
$grant_type = 'client_credentials'
if (-not $headers) {
$body = #{client_id=$client_id; scope=$scope; client_secret=$client_secret; grant_type=$grant_type}
$uri = "https://login.microsoftonline.com/$tenant/oauth2/v2.0/token"
$Response = Invoke-RestMethod -Method Post -Uri $uri -Body $body
$access_token = $Response.access_token
$headers = #{
Authorization = "$($Response.token_type) $($Response.access_token)"
ExpiresOn = $($Response.expires_in)
}
}
Write-Host "Getting Groups"
$uri = "https://graph.microsoft.com/v1.0/groups?$orderby=displayName"
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft id, displayName
#this works and returns 100 groups
foreach ($groupId in $($Response.value.id)) {
$groupId
$uri = "https://graph.microsoft.com/v1.0/groups/$groupId/planner/plans"
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft
}
I've also tried pulling tasks from a plan I know exists:
$plan_id = 'zBgxnzXTNEaGeW9Hz1CVSmQAHpg2'
$uri = "https://graph.microsoft.com/v1.0/planner/plans/$plan_id/tasks"
$uri
$Response = $null
$Response = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri
$Response.value |ft
I get the same error.
I see you are using an app secret, and therefore you are using application permissions that are not supported. Need to be delegated.
Take a look at the Docs pages for the requests, and there you see if app or delegated is supported.
Here you have two ways of getting a token for delegated access:
https://gist.githubusercontent.com/leeford/04fc4c2d4404c2a31a172923d9bed8ee/raw/294f2303b306b4bf7a31f1541ff4d59dc5b40ca2/AzureADGraphAPIUserToken.ps1
https://www.lee-ford.co.uk/graph-api-device-code/
Related
I'm working on a PowerShell script to assign users office 365 license based on group (security group). So, i have created app registration and assigned the required API permissions.
When I try to run my script, i get the error below
Invoke-RestMethod : The remote server returned an error: (400) Bad Request.
At line:1 char:1
+ Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/jso ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Below is the entire script
$connectiondetails = #{
# This ids and secret are present in the overview and certificate & secret page of our application in azure AD
# Tenant ID here
'tenantid' = ""
# Application (client) ID here
'clientid' = ""
# Secret id here
'ClientSecret' = "" | ConvertTo-SecureString -AsPlainText -Force
}
$token = Get-MsalToken #connectiondetails
$tokenid_ = $token.AccessToken
# $uri = "https://graph.microsoft.com/v1.0/groups"
# $grp = Invoke-RestMethod -Uri $uri -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
# $grp
$uri = "https://graph.microsoft.com/v1.0/groups/ffbabc6f-aa87-40f3-8665-9d140e4a7adb/assignLicense"
$body = "{""SkuId"":""cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46""}"
# assign license call
Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/json" -Method post -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
Permissions assigned to the app
I need assistance to know what am doing wrong. Thank you.
Solutions tried
The body for the request to add license requires addLicenses property with permissions Group.ReadWrite.All and Directory.ReadWrite.All.
{
"addLicenses": [
{
"skuId": "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"
}
],
"removeLicenses": []
}
PS
$uri = "https://graph.microsoft.com/v1.0/groups/ffbabc6f-aa87-40f3-8665-9d140e4a7adb/assignLicense"
# create json object
$data = #{
"addLicenses" = #(
#{
"skuId" = "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"
}
)
"removeLicenses" = #()
}
# convert to JSON-formatted string
$body = $data | ConvertTo-Json
# assign license call
Invoke-RestMethod -Uri $uri -Body $body -ContentType "application/json" -Method post -Headers #{Authorization=("bearer {0}" -f $tokenid_)}
Resources:
Group - assign licenses
This article seems to explain the script building in detail
Create MS Graph scripts with AAD App
I am trying to get all users under couple of project. The case is that under users there are many and I need to to use continuationToken. The result is messy because my logic is bad. I cannot figure out how to use the second foreach..
$outputItems = #()
foreach ($project in $projects) {
$uriProjectDescriptor = "https://vssps.dev.azure.com/$OrganizationName/_apis/graph/descriptors/$($project.id)?api-version=5.0-preview.1"
$projectDescriptor = Invoke-RestMethod -Uri $uriProjectDescriptor -Method Get -Headers $AzureDevOpsAuthenicationHeader
$descriptors = $projectDescriptor.value
foreach ($descriptor in $descriptors) {
do
{
$uriUsers="https://vssps.dev.azure.com/$OrganizationName/_apis/graph/users?continuationToken=$continuationToken&scopeDescriptor=$($descriptor)&api-version=6.0-preview.1"
$responseUsers=Invoke-WebRequest -Uri $uriUser -Method Get -ContentType "application/json" -Headers $AzureDevOpsAuthenicationHeader -UseBasicParsing -MaximumRedirection 0
$continuationToken = $responseUsers.Headers.'x-ms-continuationtoken'
$userSet = $responseUsers.content | ConvertFrom-Json
$users += $userSet.value.Count
$arealList = New-Object -TypeName PSObject -Property #{
CountUsers = $users.Count
} | Select-Object "CountUsers"
$outputItems += $arealList
$arealList = $null
}
while (($continuationToken))
$ProgressPreference = 'Continue'
}
} $outputItems
You could try the following Powershell Script:
$token = "PAT"
$url="https://dev.azure.com/{Organizationname}/_apis/projects?api-version=6.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$responses = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
ForEach ($response in $responses.value.id)
{
$url1="https://vssps.dev.azure.com/{Organizationname}/_apis/graph/descriptors/$($response)?api-version=6.0-preview.1"
$Descriptor = Invoke-RestMethod -Uri $url1 -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
ForEach ($Descriptor1 in $Descriptor.value)
{
do
{
$url2="https://vssps.dev.azure.com/{Organizationname}/_apis/graph/users?scopeDescriptor=$($Descriptor1)&api-version=6.0-preview.1"
$UserLists = Invoke-RestMethod -Uri $url2 -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
$continuationToken = $UserLists.Headers.'x-ms-continuationtoken'
}
while ($continuationToken -ne $null)
Write-Host "result = $($UserLists | ConvertTo-Json -Depth 100)"
}
}
Explaination:
The first Rest API is used to get the Project name.
Then the second Rest API is used to get the descriptors.
The third Rest api is used to get the userlist.
I use foreach nesting to realize the loop of response to Rest API.
Update:
Test with the continuationToken and the maximum value of objects returned by a project is 500.
I found another Rest Api User Entitlements - List to get the users.
https://vsaex.dev.azure.com/Organization Name/_apis/userentitlements?top=10000&select=project&api-version=4.1-preview.1
This Rest APi could directly return the users under the organization(Project Scope). The max value is 10000.
I want to edit a Azure DevOps wiki page over the REST API (Azure DevOps Server 2019.0.1).
When I run this PowerShell script:
#VARIABLES
$api = "api-version=5.0"
$root = "http://136.202.18.216:8070/Samples"
$personalToken = "uwawlzqp6j7i1nd5dasspwkwp63tr2w2sxb5563zrla2bivynbza"
#AUTHORIZATION
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$headers = #{
"Authorization" = ('Basic {0}' -f $token)
"If-Match" = '*'
}
#PROJECT VARIABLES
$project = "Framework%20A"
$pageToUpdate = "/Seite2"
#BODY
$body = #"
{
"content": "Hello"
}
"#
#GET
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&includeContent=True&$api"
$content = Invoke-RestMethod -Uri $url -Method Get -ContentType "application/json" -Headers $headers
Write-Host "$($content.path) = $($content.content)" -ForegroundColor Yellow
#PUT
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&$api"
$update = Invoke-RestMethod -Uri $url -Method Put -ContentType "application/json" -Headers $header -Body $body -Verbose
Write-Host $update.content -ForegroundColor Yellow
exit(0)
The consolen output is:
/Seite2 = Seite 2 Content
AUSFÜHRLICH: PUT http://136.202.18.216:8070/Samples/Framework A/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=/Seite2&api-version=5.0 with -1-byte payload
Invoke-RestMethod: {"$ id": "1", "innerException": null, "message": "The required \" IfMatch \ "header specified in the request is an invalid page version Version of the
Wiki page as \ "IfMatch \" header for the request. \ R \ nParameterName: IfMatch "," typeName ":" Microsoft.TeamFoundation.SourceControl.WebServer.InvalidArgumentValueException,
Microsoft.TeamFoundation.SourceControl.WebServer "," TypeKey ":" InvalidArgumentValueException "," error code ": 0," eventId ": 0}
In C:\Users\mkober\Desktop\Azure DevOps Skripte (Bearbeitung)\WikiAPI.ps1:34 Zeichen:11
+ $update = Invoke-RestMethod -Uri $url -Method Put -ContentType "appli ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
The first line /Seite2 = Seite 2 Content is the result of a successful get request from the page I want to edit. The put request occures the error. What am I doing wrong here?
UPDATE: (Working Example)
#VARIABLES
$api = "api-version=5.0"
$root = "http://136.202.18.216:8070/Samples"
$personalToken = "uwawlzqp6j7i1nd5dasspwkwp63tr2w2sxb5563zrla2bivynbza"
#AUTHORIZATION
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($personalToken)"))
$headers = #{
"Authorization" = ('Basic {0}' -f $token)
"If-Match" = '{0}'
}
#PROJECT VARIABLES
$project = "Framework%20A"
$pageToUpdate = "/Seite2"
#BODY
$body = #"
{
"content": "Hello"
}
"#
#---------------------------------------------------------------------------------------------------------------------------
#GET
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&includeContent=True&$api"
$content = Invoke-WebRequest -Uri $url -Method Get -ContentType "application/json" -Headers $headers
$etag = $content.Headers.ETag
$headers.'If-Match' = $headers.'If-Match' -f $etag
Write-Host "$($content.path) = $($content.content)" -ForegroundColor Yellow
#PUT
$url = "$root/$project/_apis/wiki/wikis/2e887fde-ce76-4180-aa8d-f26ed4799eb3/pages?version=wikiMaster&path=$pageToUpdate&$api"
$update = Invoke-RestMethod -Uri $url -Method Put -ContentType "application/json" -Headers $headers -Body $body -Verbose
Write-Host $update.content -ForegroundColor Yellow
exit(0)
#---------------------------------------------------------------------------------------------------------------------------
In the If-Match header you can't just put '*', you need to put there the ETag of the page.
How do you get it? in your first GET call use Invoke-WebRequest (instaed of Invoke-RestMethod), now in the response you will get also headers response and there the ETag exist:
$page = Invoke-WebRequest -Uri $url ...........
$etag = $page.Headers.ETag
$headers = #{
"Authorizaion = ....."
"If-Match" = $etag
}
Now the second Invoke-RestMethod will work to update the page.
I need to attach the file either xlsx or CSV to a particular incident via SNOW REST API using PowerShell script. I have tried with the below code:
if (!$script:ServiceNowCreds) {
$script:ServiceNowCreds = Get-Credential
}
$snow_url = 'https://dev652xx.service-now.com/api/now/table/incident'
$Body = #{
'number' = 'INC00xx059'
}
$result = Invoke-RestMethod -Uri $snow_url -Credential $script:ServiceNowCreds -Body $Body -ContentType "application/json"
$result.result | select sys_id, number | ForEach-Object {
$Upload_snow_url ='https://dev652xx.servicenow.com/api/now/attachment/upload'
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Content-Type','text/csv')
$headers.Add('Accept','*/*')
$sys_id = $_.sys_id
$incident_number = $_.number
$UploadBody = #{
'table_name'='incident';
'table_sys_id'=$sys_id;
'file_name' = 'C:\Users\suganthanraj.p\Documents\Servers.csv'
}
$uploadParam = $UploadBody | ConvertTo-JSon
Write-Host $sys_id
Write-Host $incident_number
$UploadResult = Invoke-RestMethod -Uri $Upload_snow_url -Credential $script:ServiceNowCreds -Body $uploadParam -Method Post -Headers $headers
$UploadResult
}
When I execute the above script I am getting the below error:
Invoke-RestMethod : The remote server returned an error: (415) Unsupported
Media Type.
At C:\Users\suganthanraj.p\Desktop\SNOW-UploadAttachment.ps1:39 char:21
+ ... oadResult = Invoke-RestMethod -Uri $Upload_snow_url -Credential $scr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Try changing you content type to "multipart/form-data"
$headers.Add('Content-Type','multipart/form-data')
$UploadBody = #{
'table_name'='incident';
'record_sys_id'=$sys_id;
'uploadFile' = 'C:\Users\suganthanraj.p\Documents\Servers.csv'
}
The error says "The remote server returned an error: (415) Unsupported
Media Type."
Doco on the api can be found here:
https://docs.servicenow.com/bundle/geneva-servicenow-platform/page/integrate/inbound_rest/reference/r_AttachmentAPI-POSTmultipart.html
Your best option would be leverage the OOB Attachment API in ServiceNow. You will need to make a post call from powershell. Powershell has two options for this Invoke-RestMethod and Invoke-WebRequest. I have had better luck with the latter when trying to POST. You might also first build your rest call in Postman make sure you can get the attachment into ServiceNow, then worry about writing your PS.
$Body = #{
User = 'jdoe'
password = 'P#S$w0rd!'
}
$LoginResponse = Invoke-WebRequest 'http://www.contoso.com/login/' - SessionVariable 'Session' -Body $Body -Method 'POST'
$Session
$ProfileResponse = Invoke-WebRequest 'http://www.contoso.com/profile/' -`WebSession $Session $ProfileResponse`
Finally i found answer from the below link
https://community.servicenow.com/community?id=community_question&sys_id=d3707023dbaceb8023f4a345ca961949 and below is the code:
# Eg. User name="admin", Password="admin" for this code sample.
$user = "admin"
$pass = "XXX"
# Build auth header
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))
# Set proper headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
$headers.Add('Accept','application/json')
$headers.Add('Content-Type','application/json')
# Specify endpoint uri
$uri = "https://dev652XX.service-now.com/api/now/attachment/file?table_name=incident&table_sys_id=850XXXXX2200e0ef563dbb9a71c1&file_name=TreeSizeReport.csv"
# Specifiy file to attach
$fileToAttach = "C:\Users\suganthanraj.p\Desktop\TreeSizeReport.csv"
# Specify HTTP method (POST, PATCH, PUT)
$method = "POST"
# Send HTTP request
$response = Invoke-WebRequest -Headers $headers -Method $method -Uri $uri -InFile $fileToAttach
# Print response
$response.RawContent
I've this command:
curl -s -L --header "PRIVATE-TOKEN:XXXX" https://myuri/"
And I need to convert to PowerShell
I've tried this, but doesn't works:
Invoke-RestMethod -Method Get -Headers #{"AUTHORIZATION"="XXXXXX"} -Uri https://myUri
I've also tried this:
PS > $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
PS > $headers.Add("Authorization","XXXXXX")
PS > $headers.Add("Accept","application/json")
PS > $headers.Add("Content-Type","application/json")
PS> $uri = "https://myUri"
PS> $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -ContentType "application/json"
PS> $response
PS>
but the $response is empty.
Any help ?
Here is one that I used to authenticate to Teamcity
function Connect-to-Teamcity ($userName, $password, $tcServer, $uri)
{
$auth = $username + ':' + $password
$Encoded = [System.Text.Encoding]::UTF8.GetBytes($auth)
$EncodedPassword = [System.Convert]::ToBase64String($Encoded)
$headers = #{"Authorization"="Basic $($EncodedPassword)"}
$url = "http://$tcServer/httpAuth/app/rest"
$result = Invoke-RestMethod -Uri "$url/$uri" -Header $headers -Method Get
return $result
}
Here is another example if you have more than one header Item:
$resourceAppIdURI = "https://graph.windows.net"
# Login to Azure and get a token valid for accessing the graph API
$authority = "https://login.windows.net/$adTenant"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Auto")
# Add the token to the header of all future calls to the graph API
$headers = #{"Authorization"=$authResult.CreateAuthorizationHeader();"Content-Type"="application/json"}
$uri = [string]::Format("https://graph.windows.net/{0}/groups/{1}/appRoleAssignments?api-version=1.5", $adTenant, $groupId)
$body = #"
{
"id": $appRoleId,
"principalId": $groupId,
"principalType": "Group",
"resourceId": $appObjectId
}
"#
$result = Invoke-RestMethod -Method "POST" -Uri $uri -Headers $headers -Body $Body