I thought I had blogged about this in the past but I am mistaken.
I see the following HTTP related connectors in Power Automate.
Which of these can I use to say talk to Graph API authenticated (i.e. without having to fetch a token first via a regular HTTP request)?
Am going to ignore the LMS one for now as I don’t know what that is (bad idea: don’t ignore things coz you don’t know what they are… but I am going to do that now anyways).
Send an HTTP request to SharePoint
I know this isn’t going to help me but I’d like to point it out. This connector can be used to make REST API calls against SharePoint.
I have a colleague who uses this extensively to do various SharePoint stuff. I haven’t bothered so far; I just use Graph or PnP.PowerShell for this. This page looks like a good starting point with some examples.
Send an HTTP Request (Outlook)
Can I do a simple Graph query with it?
Nope! I get the following error:
URI path is not a valid Graph endpoint, path is neither absolute nor relative or resource/object is not supported for this connector. Resources: me,users Objects: messages,mailFolders,events,calendar,calendars,outlook,inferenceClassification. Uri: https://graph.micrsosoft.com/v1.0/me
So it’s for a limited set of objects only – like messages
for instance. Modifying the URL to https://graph.microsoft.com/v1.0/me/messages
works.
Send an HTTP Request (Users)
Same error:
URI path is not a valid Graph endpoint, path is neither absolute nor relative or resource/object is not supported for this connector. Resources: me,users Objects: messages,mailFolders,events,calendar,calendars,outlook,inferenceClassification. Uri: https://graph.microsoft.com/v1.0/me
I thought I’d get a different error message, or at least the /me
endpoint would succeed – this one’s for users after all. I know my future self reading this blog post wouldn’t trust me, so here’s a screenshot too – I didn’t select the wrong connector by mistake. :)
Send an HTTP Request (Groups)
There seem to be two of these. Am going to go with the V2.
Hmm:
URI path is not a valid Graph endpoint, path is neither absolute nor relative or resource/object is not supported for this connector. Resources: groups. Uri: https://graph.microsoft.com/v1.0/me
Ok what about the non-V2? Ooh, that worked! Finally.
Let’s try a different query? Like say /me/calendar/events
. I want to get an error to see what is allowed.
Bugger, that worked too. Ok what about Teams presence? /me/presence
.
Finally it failed. But with a different error to what I was expecting:
1 2 3 4 5 6 7 8 9 10 11 |
{ "error": { "code": "Forbidden", "message": "", "innerError": { "request-id": "6007b16a-63fe-4a22-afe4-7e91e8ab2fc5", "date": "2022-10-30T13:46:23", "client-request-id": "6007b16a-63fe-4a22-afe4-7e91e8ab2fc5" } } } |
Moving on… (but note to myself, this connector is worth keeping in mind for the future. “Send an HTTP Request” (groups))
Send an HTTP Request (Groups Mail)
Again, an error:
URI path is not a valid Graph endpoint, path is neither absolute nor relative or resource/object is not supported for this connector. Resources: groups Objects: messages,mailFolders,events,calendar,calendars,outlook,inferenceClassification. Uri: https://graph.microsoft.com/v1.0/me
Looks similar to the errors I go initially, except this one is for groups rather than users.
Invoke an HTTP Request (Azure AD)
I have used this one in the past. Here and here. It needs the following pieces of info to create a connection:
The Base URL is the base URL. But it must be in the form of a GUID?
And the Azure AD Resource URI is usually the Base URL, but it could be an identifier.
For Key Vaults I have done:
- Base URL:
https://<keyvaultname>.vault.azure.net/
- Azure AD Resource URI:
https://vault.azure.net
(no trailing slash; if you add a slash you get an error “AKV10022: Invalid audience. Expected https://vault.azure.net, found: https://vault.azure.net/.
“)
For Forms I have done:
- Base URL:
https://forms.microsoft.com/
- Azure AD Resource URI: The client_id of the Microsoft Forms enterprise application (
c
9a559d2-7aab-4f13-a6ed-e7e9c52aec87
)
In general Base URL is the base URL of whatever you are trying to connect to. For example, if I am doing https://graph.microsoft.com/v1.0/users/<userId>/getPresence
, the Base URL would be https://graph.microsoft.com/
or https://graph.microsoft.com/v1.0/
.
From a quick Google now I found this post with some more examples:
For SharePoint:
- Base URL:
https://<tenant>.sharepoint.com
- Azure AD Resource URI:
https://<tenant>.sharepoint.com/sites/<siteName>/Lists
(maybe I do justhttps://<tenant>.sharepoint.com/sites/<siteName>
– should try sometime)
For Power BI:
- Base URL:
https://api.powerbi.com
- Azure AD Resource URI:
https://analysis.windows.net/powerbi/api
In my case I want to do Graph. So I create a connection as above, sign in, and try to run a query:
That works!
Let’s try the presence one: https://graph.microsoft.com/v1.0/me/presence
Fails, with a similar forbidden message to the one I got above for one of the connectors:
1 2 3 4 5 6 7 8 9 10 11 |
{ "error": { "code": "Forbidden", "message": "", "innerError": { "request-id": "d552a5b4-5332-45f9-bcf1-c9c12e4b32dd", "date": "2022-10-30T14:08:59", "client-request-id": "d552a5b4-5332-45f9-bcf1-c9c12e4b32dd" } } } |
Thankfully, here that doc I called useless above has some info:
So its a scopes issue. Nice.
Now I know why I was getting two set of error messages. Some connectors know what they can accept, and so they respond more helpfully if you ask it for something it can’t do. Other connectors aren’t programmed that way; they take whatever you give, but then the underlying Azure AD layer complains when the connector isn’t scoped for that and so the connector replies with a forbidden message. Makes sense.
Let’s also explore Custom Connectors while I am at it.
Custom Connectors
In this case I want a custom connector that can get presence info. I want to do what I blogged about previously using PowerShell, but now using Power Automate.
Custom Connectors look very interesting. Especially this one that talks to an Azure Function using authentication – I must explore it more later. Let me create one for Graph API… going to wing it.
In Power Automate, go to Custom Connectors and create a new blank one.
Give it a name: “Presence API – Graph”
I set a description: “Gets and sets Presence info via Graph API”
The default scheme of “HTTPS” sounds sensible. Set Host as “graph.microsoft.com”.
Go to the next page (Security).
I want OAuth 2.0.
Ah, I must now create an App Registration and fill its details. Not going to go into details on that (check out this official doc). I had to make one for my prior post anyways so I’ll just reuse that with some changes.
Very briefly though, 1) I created an App Registration. 2) Gave it the delegated Presence.Read and Presence.ReadWrite permissions. 3) Did admin consents for these. All of these I had done previously too, but now I 4) Created a client secret too.
Add these under the OAuth 2.0 section:
I added the scopes too further down.
For the three URLs, I wasn’t sure but went with “https://login.microsoftonline.com/
“. I wasn’t sure if that is enough or whether to add the rest such as “/{tenant}/oauth2/v2.0/authorize
” too.
I then did a “Create Connector” coz it sounds like I should do that to get the redirect URL. Might as well get that and add it to the App Registration while I have that open.
The redirect URL seems to be a standard one: https://global.consent.azure-apim.net/redirect
– I added that to the App Registration.
Then I went to the next page (Definition).
Sounds like I need an action to begin with:
I did “New action”. I filled it thus:
Next we have these two:
If I click on “Import form sample” under “Request” I get this:
Ok, so that seems to be the Graph query. Let’s fill it thus:
Then I did “Add default response” under the “Response” section.
That just needs a sample response data. Easy, I have that in the Graph API page.
I hit “Update Connector”.
Go back to my Flow, add a new Custom connector, and there it is:
If I select, I can see the action I created:
So far so good. If I add it I am asked to sign in:
Hmm that didn’t work. It signed me in by opening a new window, but after I signed in it to me to office.com and Power Automate wasn’t happy:
At this point I Googled on how to create custom connectors. I think I was on the right track, but couple of blog posts I found had https://login.windows.net
as the URL so I switched to that instead.
That didn’t help, and I don’t think it matters anyways. The two are identical I think.
Not sure what to do I poked around the Custom Connector further. I changed the above URLs back but then realized that the Resource URL can’t be https://login.microsoftonline.com/. So I changed that to https://graph.microsoft.com (skipped the trailing “/” based on what I remembered from the Key Vault experience above).
Does that help?
Damn! It did. :)
And it works too. :)
Ok can I add an action now to set a presence too? I am curious!
Add a new action:
Not sure how to proceed further.
The request needs a User ID in the URL itself. Like this: https://graph.microsoft.com/v1.0/users/fa8bf3dc-eca7-46b7-bad1-db199b62afc3/presence/setPresence
How do I get that User ID?
Looking around, I came across this helpful doc I had previously missed. That didn’t help but it taught me a few more things I had missed. Then I came across this forum post.
Trick is to set the URL as https://graph.microsoft.com/v1.0/users/{UserId}/presence/setPresence
– that will make UserId an input parameter.
Also, for the body I don’t need to provide any input values – I just specify the type as above.
I can see UserId is an input now:
Let me edit that:
I can add a description etc.
There’s also a drop down to change the type:
Similarly I edit the “body”. And within that the “activity”:
I don’t want to take this as an input. So I set it a default value, changed it to required (couldn’t save without doing this), and set visibility to hidden so it isn’t taken as an input.
Ditto for availability and sessionid.
The only input I want to take is expirationDuration so I added a description for that and also a default value.
After this I gave up… spent nearly 30 mins waiting for the new action to be picked up in my test Flow, but it didn’t happen. I changed the icon, and that got picked up after 10 mins; but nothing else.
My actions:
What I see:
This is from a freshly created Flow too.
Looks like custom connector updates can take up to a day? WTF!?
Many hours later…
I got tired waiting. So I exported the Custom Connector and added it to a new environment. Created a Flow there and now I can see the action.
That doesn’t look nice so I changed the description (shown as the gray text) and summary (shown as the field name):
Not going to bother with a new screenshot as it hasn’t updated yet lol.
Also changed the text for expiryDuration.
I tested the connector after doing this, and it works as expected. :)
Connector Export
An export of the connector:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
{ "swagger": "2.0", "info": { "title": "Presence API - Graph", "description": "Gets and sets Presence info via Graph API", "version": "1.0" }, "host": "graph.microsoft.com", "basePath": "/", "schemes": [ "https" ], "consumes": [], "produces": [], "paths": { "/v1.0/me/presence": { "get": { "responses": { "default": { "description": "default", "schema": { "type": "object", "properties": { "id": { "type": "string", "description": "id" }, "availability": { "type": "string", "description": "availability" }, "activity": { "type": "string", "description": "activity" } } } } }, "summary": "Get Presence", "description": "Gets the Presence Info of the signed in user", "operationId": "GetPresence", "parameters": [] } }, "/v1.0/users/{UserId}/presence/setPresence": { "post": { "responses": { "default": { "description": "default", "schema": { "type": "object", "properties": {} } } }, "summary": "Set Presence", "description": "Sets the Presence Info of the signed in user", "operationId": "SetPresence", "parameters": [ { "name": "UserId", "in": "path", "required": true, "type": "string", "description": "Enter the User ID as a GUID", "x-ms-summary": "User ID" }, { "name": "body", "in": "body", "required": false, "schema": { "type": "object", "properties": { "sessionId": { "type": "string", "description": "sessionId", "title": "sessionId", "default": "cb7afc8a-0c52-49c6-9d4d-2f537c697869", "x-ms-visibility": "internal" }, "availability": { "type": "string", "description": "availability", "title": "", "default": "DoNotDisturb", "x-ms-visibility": "internal" }, "activity": { "type": "string", "description": "activity", "title": "", "x-ms-visibility": "internal", "default": "Presenting" }, "expirationDuration": { "type": "string", "description": "How long should the status be set for?", "title": "Expiry Duration", "default": "PT2H" } }, "required": [ "activity", "availability", "expirationDuration", "sessionId" ] } } ] } }, "/https;//graph.microsoft.com/v1.0/me": {} }, "definitions": {}, "parameters": {}, "responses": {}, "securityDefinitions": { "oauth2_auth": { "type": "oauth2", "flow": "accessCode", "authorizationUrl": "https://login.windows.net/common/oauth2/authorize", "tokenUrl": "https://login.windows.net", "scopes": { "Presence.Read Presence.ReadWrite": "Presence.Read Presence.ReadWrite" } } }, "security": [ { "oauth2_auth": [ "Presence.Read Presence.ReadWrite" ] } ], "tags": [] } |
Save this as JSON.
Import as OpenAPI file. Give it a name.
Fill the client id, client secret, and resource URL (https://graph.microsoft.com
). And of course, create an App Registration as that’s where you get the client id and secret from.