Microsoft Graph OneDrive invite fails when inviting group alias - microsoft-graph-api

We are seeing what appears to be a change in the behavior of the Graph API. Our code, which grants access for an Excel workbook in OneDrive to a specific Security Group, is now failing where it used to succeed.
Request:
POST https://graph.microsoft.com/V1.0/groups/d4826b5d-4106-40a6-97e0-3826dff58e17/drive/root:/sageData/_verbs.xlsx:/invite HTTP/1.1
Accept: application/json
Authorization: Bearer <<token omitted>>
Content-Type: application/json; charset=utf-8
Host: graph.microsoft.com
Content-Length: 127
Expect: 100-continue
Connection: Keep-Alive
{
"recipients": [{
"alias": "d536e908-60cb-4558-8b3a-38f033d6508a"
}],
"requireSignIn": true,
"sendInvitation": false,
"roles": ["Write"]
}
Response:
HTTP/1.1 404 Not Found
Cache-Control: private
Content-Type: application/json
request-id: 48f148b6-0c15-410e-b29d-bef5880c7007
client-request-id: 48f148b6-0c15-410e-b29d-bef5880c7007
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"North Europe","Slice":"SliceB","Ring":"NA","ScaleUnit":"001","Host":"AGSFE_IN_14","ADSiteName":"DUB"}}
Duration: 981.5167
Date: Wed, 15 Nov 2017 13:05:50 GMT
Content-Length: 247
{
"error": {
"code": "itemNotFound",
"message": "One of the provided recipients could not be found",
"innerError": {
"request-id": "48f148b6-0c15-410e-b29d-bef5880c7007",
"date": "2017-11-15T13:05:51"
}
}
}
However, we can see that "recipient"" (which is the Security Group id) does indeed exist:
GET https://graph.microsoft.com/v1.0/groups/d536e908-60cb-4558-8b3a-38f033d6508a
Response
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups/$entity",
"id": "d536e908-60cb-4558-8b3a-38f033d6508a",
"deletedDateTime": null,
"classification": null,
"createdDateTime": "2017-11-15T12:42:01Z",
"description": "{\"datasetIdentifier\":\"4122e61b-d5c1-4a58-9068-dfdeda9e8278\",\"roleIdentifier\":\"FullAccess\",\"isSageMetadata\":true,\"type\":\"Role\"}",
"displayName": "Sage - Pete - All Apps & Add-Ins",
"groupTypes": [],
"mail": null,
"mailEnabled": false,
"mailNickname": "Sage-Pete-AllApps-Add-Ins",
"onPremisesLastSyncDateTime": null,
"onPremisesProvisioningErrors": [],
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"proxyAddresses": [],
"renewedDateTime": "2017-11-15T12:42:01Z",
"securityEnabled": true,
"visibility": null
}
The documentation states the recipient property we are posting should be "email" whereas we have used "alias" up to this point. If we change to "email" then it works.
However, that seems incorrect, according to the documentation we should use alias:
"The alias of the domain object, for cases where an email address is unavailable (e.g. security groups)."
Has anyone else encountered this behaviour?

What you're submitting as an alias is actually an objectId. The alias in this case would be the object's mailNickname (i.e. Sage-Pete-AllApps-Add-Ins).
Either of these should work:
{
"recipients": [{
"objectId": "d536e908-60cb-4558-8b3a-38f033d6508a"
}],
"requireSignIn": true,
"sendInvitation": false,
"roles": ["Write"]
}
or
{
"recipients": [{
"alias": "Sage-Pete-AllApps-Add-Ins"
}],
"requireSignIn": true,
"sendInvitation": false,
"roles": ["Write"]
}

Related

How to move to next page of results in Microsoft Graph Search API when searching calendar events?

With Microsoft Graph API you can search your calendar events by issuing a request like it's documented here or like this:
POST /v1.0/search/query HTTP/1.1
Host: graph.microsoft.com
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: 174
{
"requests": [
{
"entityTypes": [
"event"
],
"query": {
"queryString": "test"
},
"from": 0,
"size": 50
}
]
}
So, in my case, for the search query test, I have more than 200 events (recurring and single ones), and in the request payload we've sent to Microsoft Graph we have the size parameter which basically sets the limit of records to be returned by this call.
So, when I issue the request from above, I get something like this, but never more than 25 results. No matter how I modify the size parameter, it always returns 25 events, although there are hundreds.
HTTP/1.1 200 OK
Content-type: application/json
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#search",
"value": [
{
"#odata.type": "#microsoft.graph.searchResponse",
"searchTerms": [
"contoso"
],
"hitsContainers": [
{
"#odata.type": "#microsoft.graph.searchHitsContainer",
"hits": [
{
"#odata.type": "#microsoft.graph.searchHit",
"hitId": "AAMkADEwODY2NzllLTQ3MmEtNGRlMC05ZTUyLTE4ZDRhYmU1ZGM3NABGAAAAAAA3+iYQBnJnQabRVDelNhnzBwAejhWkAOAxQ6M4c1c9NwfrAAAAAAENAAAejhWkAOAxQ6M4c1c9NwfrAABbUZLJAAA=",
"rank": 1,
"summary": "this is a testing nothing more",
"resource": {
"#odata.type": "#microsoft.graph.event",
"end": {
"dateTime": "2020-06-16T04:15:00Z",
"timeZone": "UTC"
},
"hasAttachments": false,
"iCalUId": "040000008200E00074C5B7101A82E008000000007093FDD79B3AD60100000000000000001000000036DAA2262EB4E04DA27DA77985FB8251",
"isAllDay": false,
"sensitivity": "Normal",
"start": {
"dateTime": "2020-06-16T03:30:00Z",
"timeZone": "UTC"
},
"subject": "testing testing 123",
"type": "Single"
}
}
],
"total": 25,
"moreResultsAvailable": true
}
]
}
]
}
Furthermore, in the response you get the flag moreResultsAvailable which is in my case true, but other than that, I can't find a way to traverse between the results page, or do pagination of some sort.
What am I missing here, or in the docs?
So, when I issue the request from above, I get something like this,
but never more than 25 results. No matter how I modify the size
parameter, it always returns 25 events, although there are hundreds.
As per https://learn.microsoft.com/en-us/graph/api/resources/search-api-overview?view=graph-rest-1.0#page-search-results
The maximum results per page (size) is 25 for message and event.
For paging you just need to use the from property to specify the start of the page eg for the next page it would be
{
"requests": [
{
"entityTypes": [
"event"
],
"query": {
"queryString": "test"
},
"from": 25,
"size": 25
}
]
}

How to create Microsoft Team via Microsoft Graph API with Owners specified by User Email rather than User ID?

Desired Behaviour
Create a Microsoft Team, with specified Owners and Members, in a Power Automate Flow using the HTTP connector to make requests to the Microsoft Graph API.
Actual Behaviour
This code shows how you can create a Microsoft Team whilst specifying it's Owners by User ID.
{
"template#odata.bind": "https://graph.microsoft.com/v1.0/teamsTemplates('standard')",
"displayName": "My Sample Team",
"description": "My Sample Team’s Description",
"members": [
{
"#odata.type": "#microsoft.graph.aadUserConversationMember",
"roles": [
"owner"
],
"user#odata.bind": "https://graph.microsoft.com/v1.0/users('0040b377-61d8-43db-94f5-81374122dc7e')"
}]
}
Source
For reference, the Graph API docs entry for Get a user (which is used to specify the Owner in the code above), specifies the endpoint syntax as:
GET /users/{id | userPrincipalName}
Currently, I do not have access to users' id or userPrincipalName.
What I've Tried
I am using a Power Automate Flow with the SharePoint For a selected item trigger.
The list includes two columns of type Person where Owners and Members are specified.
In order to get a list of Owners, I am using the SharePoint connector's Get item action on the selected item and then using an Apply to each control to iterate over each columns' values.
Below is the Raw Output of Get Item - the Owners column array is TeamOwner_x002f_s.
You can see that each object only has DisplayName and Email and not User ID or userPrincipalName:
{
"statusCode": 200,
"headers": {
"Transfer-Encoding": "chunked",
"Vary": "Origin,Accept-Encoding",
"X-SharePointHealthScore": "1",
"X-MS-SPConnector": "1",
"X-SP-SERVERSTATE": "ReadOnly=0",
"DATASERVICEVERSION": "3.0",
"SPClientServiceRequestDuration": "68",
"SPRequestGuid": "letters-and-numbers",
"request-id": "letters-and-numbers",
"MS-CV": "letters-and-numbers.0",
"Strict-Transport-Security": "max-age=31536000",
"X-FRAME-OPTIONS": "SAMEORIGIN",
"Content-Security-Policy": "frame-ancestors 'self' teams.microsoft.com *.teams.microsoft.com *.skype.com *.teams.microsoft.us local.teams.office.com *.powerapps.com *.yammer.com *.officeapps.live.com *.office.com *.stream.azure-test.net *.microsoftstream.com;",
"MicrosoftSharePointTeamServices": "numbers-and-dots",
"X-Content-Type-Options": "nosniff",
"X-MS-InvokeApp": "1; RequireReadOnly",
"Timing-Allow-Origin": "*",
"x-ms-apihub-cached-response": "false",
"Cache-Control": "max-age=0, private",
"Date": "Sun, 04 Jul 2021 06:42:53 GMT",
"P3P": "CP=\"SOME CODES HERE\"",
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"Content-Type": "application/json; charset=utf-8",
"Expires": "Sat, 19 Jun 2021 06:42:54 GMT",
"Last-Modified": "Sun, 04 Jul 2021 06:42:54 GMT",
"Content-Length": "3938"
},
"body": {
"#odata.etag": "\"2\"",
"ItemInternalId": "1",
"ID": 1,
"Title": "View",
"Year": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
"Id": 2,
"Value": "2023"
},
"Year#Id": 2,
"ProgramType": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
"Id": 0,
"Value": "Program Type 01"
},
"ProgramType#Id": 0,
"ProgramName": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
"Id": 1,
"Value": "02"
},
"ProgramName#Id": 1,
"TeamOwner_x002f_s": [
{
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"DisplayName": "User Name 1",
"Email": "user_1#my-tenant.onmicrosoft.com",
"Picture": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/UserPhoto.aspx?Size=L&AccountName=user_1#my-tenant.onmicrosoft.com",
"Department": null,
"JobTitle": null
},
{
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|user_2#my-tenant.onmicrosoft.com",
"DisplayName": "User Name 2",
"Email": "user_2#my-tenant.onmicrosoft.com",
"Picture": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/UserPhoto.aspx?Size=L&AccountName=user_2#my-tenant.onmicrosoft.com",
"Department": null,
"JobTitle": null
}
],
"TeamOwner_x002f_s#odata.type": "#Collection(Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser)",
"TeamOwner_x002f_s#Claims": [
"i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"i:0#.f|membership|user_2#my-tenant.onmicrosoft.com"
],
"TeamOwner_x002f_s#Claims#odata.type": "#Collection(String)",
"Team_x0020_Member_x002f_s": [
{
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|user_3#my-tenant.onmicrosoft.com",
"DisplayName": "User Name 3",
"Email": "user_3#my-tenant.onmicrosoft.com",
"Picture": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/UserPhoto.aspx?Size=L&AccountName=user_3#my-tenant.onmicrosoft.com",
"Department": null,
"JobTitle": null
}
],
"Team_x0020_Member_x002f_s#odata.type": "#Collection(Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser)",
"Team_x0020_Member_x002f_s#Claims": [
"i:0#.f|membership|user_3#my-tenant.onmicrosoft.com"
],
"Team_x0020_Member_x002f_s#Claims#odata.type": "#Collection(String)",
"Status": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedReference",
"Id": 0,
"Value": "Details Added"
},
"Status#Id": 0,
"Modified": "2021-07-04T03:57:16Z",
"Created": "2021-07-04T03:51:18Z",
"Author": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"DisplayName": "User Name 1",
"Email": "user_1#my-tenant.onmicrosoft.com",
"Picture": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/UserPhoto.aspx?Size=L&AccountName=user_1#my-tenant.onmicrosoft.com",
"Department": null,
"JobTitle": null
},
"Author#Claims": "i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"Editor": {
"#odata.type": "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
"Claims": "i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"DisplayName": "User Name 1",
"Email": "user_1#my-tenant.onmicrosoft.com",
"Picture": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/UserPhoto.aspx?Size=L&AccountName=user_1#my-tenant.onmicrosoft.com",
"Department": null,
"JobTitle": null
},
"Editor#Claims": "i:0#.f|membership|user_1#my-tenant.onmicrosoft.com",
"{Identifier}": "Lists%252fMy%2bList%2bName%252f1_.000",
"{IsFolder}": false,
"{Thumbnail}": {
"Large": null,
"Medium": null,
"Small": null
},
"{Link}": "https://my-tenant.sharepoint.com/sites/ExampleTeam/_layouts/15/listform.aspx?PageType=4&ListId=list-id-here&ID=1&ContentTypeID=content-type-id-here",
"{Name}": "View",
"{FilenameWithExtension}": "View",
"{Path}": "Lists/My List Name/",
"{FullPath}": "Lists/My List Name/1_.000",
"{VersionNumber}": "2.0"
}
}
Question
Is it possible to specify Team members by Email in the Graph API request?
Or do I need to somehow get the User ID of each user specified (and if so, what is the best way to do that)?
Update
I tried this HTTP connector GET request in the hopes it would return User ID for each Person:
https://graph.microsoft.com/v1.0/sites/site-id-here/lists/list-id-here/items/item-id-here?expand=fields(select=TeamOwner%5Fx002f%5Fs)
But it returned even less information:
"fields": {
"#odata.etag": "\"letters-and-numbers,2\"",
"TeamOwner_x002f_s": [{
"LookupId": 9,
"LookupValue": "User Name 1",
"Email": "user_1#my-tenant.onmicrosoft.com"
},
{
"LookupId": 27,
"LookupValue": "User Name 2",
"Email": "user_2#my-tenant.onmicrosoft.com"
}
]
}
API docs reference for this approach
Get specific column values of a listItem
GET https://graph.microsoft.com/v1.0/sites/{site-id}/lists/{list-id}/items/{item-id}?expand=fields(select=Column1,Column2)
You can get the user information from Microsoft Graph itself, what you can try out is List Users with filter parameter.
like this:
GET https://graph.microsoft.com/v1.0/users?$filter(mail eq 'user_1#my-tenant.onmicrosoft.com')
OR
You can list all users and do a mail/displayName search(Ctrl+F) on results at graph explorer.
By these you will be able to get userPrincipalName as well as id.
Permission required:-
One of the following permissions is required to call this API.
Delegated (work or school account) - User.ReadBasic.All,
User.Read.All, User.ReadWrite.All, Directory.Read.All,
Directory.ReadWrite.All, Directory.AccessAsUser.All
Delegated
(personal Microsoft account) - Not supported.
Application - User.Read.All, User.ReadWrite.All, Directory.Read.All,
Directory.ReadWrite.All
Thanks.

Power Automate / Twilio : HTTP POST Error

I'm not sure what I've messed up, but I'm receiving this error when attempting to send a POST request from Power Automate to a Twilio Flow.
Was able to trigger the Twilio Flow from PowerShell, but cannot replicate on Power Automate.
{
"code": 20001,
"message": "Missing required parameter To in the post body",
"more_info": "https://www.twilio.com/docs/errors/20001",
"status": 400
}
input
output
http request post
This ended up working for me.
Change Content-Type value to
application/x-www-form-urlencoded; charset=utf-8
Change Body value to
To=%2B12223334444&From=%2B15556667777
12223334444 = Send To #
15556667777 = Send From # (Twilio Phone # assigned the Flow)
Re: Using HTTP POST with Twilio
INPUT
{
"uri": "https://studio.twilio.com/v2/Flows/##################################/Executions",
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded; charset=utf-8"
},
"authentication": {
"username": "**********************************",
"password": "*sanitized*",
"type": "Basic"
},
"body": "To=%2B12223334444&From=%2B15556667777"
}
OUTPUT
{
"statusCode": 201,
"headers": {
"Connection": "keep-alive",
"Twilio-Concurrent-Requests": "1",
"Twilio-Request-Id": "##################################",
"Twilio-Request-Duration": "0.055",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Accept, Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since",
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
"Access-Control-Expose-Headers": "ETag",
"Access-Control-Allow-Credentials": "true",
"X-Shenanigans": "none",
"X-Home-Region": "us1",
"X-API-Domain": "studio.twilio.com",
"Strict-Transport-Security": "max-age=31536000",
"Date": "Tue, 08 Jun 2021 14:04:24 GMT",
"X-Powered-By": "AT-5000",
"Content-Length": "707",
"Content-Type": "application/json"
},
"body": {
"status": "active",
"date_updated": null,
"contact_channel_address": "+12223334444",
"account_sid": ""**********************************",",
"url": "https://studio.twilio.com/v2/Flows/##################################/Executions/##################################",
"context": {},
"sid": "##################################",
"date_created": "2021-06-08T14:04:23Z",
"flow_sid": "##################################",
"links": {
"steps": "https://studio.twilio.com/v2/Flows/##################################/Executions/##################################/Steps",
"execution_context": "https://studio.twilio.com/v2/Flows/##################################/Executions/##################################/Context"
}
}
}

Create a message with itemAttachment

Trying to send an email through Microsoft Graph where another email from user mailbox is to be used as an attachment.
What I am doing is something similar to:
POST https://graph.microsoft.com/v1.0/me/sendMail HTTP/1.1
authorization: bearer {access_token}
content-type: application/json
content-length: 96
{
"message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "Text",
"content": "The new cafeteria is open."
},
"toRecipients": [{
"emailAddress": {
"address": "garthf#a830edad9050849NDA1.onmicrosoft.com"
}
}],
"attachments": [
"#odata.type": "#Microsoft.OutlookServices.ItemAttachment",
"name": "menu.txt",
"item": {
"id": "some_id"
<!--This is the id of an existing email in User's inbox -->
}
]
},
"saveToSentItems": "false"
}
This returns with
Cannot process input of abstract type Microsoft.OutlookServices.Item
Can anyone help with suggestions?

YouTube API v3 - Update the item's position in the playlist (PlaylistItems: update)

How do I update the item's position in the playlist?
I used API Explorer:
https://developers.google.com/youtube/v3/docs/playlistItems/update#try-it
part: snippet
Request body:
{
"id": "12345",
"snippet":
{
"playlistId": "my_playlist_id",
"resourceId":
{
"kind": "youtube#video",
"videoId": "my_video_id"
},
"position": 3
}
}
Response:
403 Forbidden
Cache-Control: private, max-age=0
Content-Encoding: gzip
Content-Length: 125
Content-Type: application/json; charset=UTF-8
Date: Sun, 28 Apr 2013 14:23:37 GMT
Expires: Sun, 28 Apr 2013 14:23:37 GMT
Server: GSE
{
"error": {
"errors": [
{
"domain": "youtube.common",
"reason": "forbidden",
"message": "Forbidden"
}
],
"code": 403,
"message": "Forbidden"
}
}
I tried it and I think the API call works correctly:
Request
PUT https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails%2Cstatus&fields=id%2Csnippet&key={YOUR_API_KEY}
Content-Type: application/json
X-JavaScript-User-Agent: Google APIs Explorer
{
"id": "PLSvpjjbPJQDXYjfamKNrJHvyvkBfr7n1QwkVWH-4kWwk",
"snippet": {
"playlistId": "PLF-2B3_5y4x_yqhdqjdrXuse2AcK7i3XE",
"resourceId": {
"kind": "youtube#video",
"videoId": "MP1gvGcXcLk"
},
"position": 1
}
}
Response
200 OK
- Show headers -
{
"id": "PLSvpjjbPJQDXYjfamKNrJHvyvkBfr7n1QwkVWH-4kWwk",
"snippet": {
"publishedAt": "2013-03-01T16:33:20.000Z",
"channelId": "UC4_r417bYTsCoaVGgH1yYTg",
"title": "Amazing Google Glasses Demonstration at Google I/O 2012",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/MP1gvGcXcLk/default.jpg"
},
"medium": {
"url": "https://i.ytimg.com/vi/MP1gvGcXcLk/mqdefault.jpg"
},
"high": {
"url": "https://i.ytimg.com/vi/MP1gvGcXcLk/hqdefault.jpg"
}
},
"channelTitle": "Matias Molinas",
"playlistId": "PLF-2B3_5y4x_yqhdqjdrXuse2AcK7i3XE",
"position": 1,
"resourceId": {
"kind": "youtube#video",
"videoId": "MP1gvGcXcLk"
}
}
}
maybe it is a quota error:
The remote server returned an error: (403) Forbidden in Youtube api
HTTP 403 responses can be associated with a number of different errors, and taking a look at the actual response HTTP body (or the serialized version of it reported in the exception.Response property) is the only way of determining the exact cause.
A very common cause of 403s when performing uploads are quota errors, though. Here are more details:
http://apiblog.youtube.com/2010/02/best-practices-for-avoiding-quota.html

Resources