Automating Azure AD B2B Invites with Approval Workflow

The Problem

Users want to collaborate with external parties. You have a few options to accomplish this:

  • Share anonymously
  • Share with any authenticated user
  • Share with existing authenticated user
  • Share with organization only

None of these work well when security and scale are required.

The best of both worlds would be to allow your end users to request access for an external user and then to kick off an approval workflow. If approved by the appropriate parties, a B2B invite will be automatically sent to the desired email address. Once invited, the user can then share with that account. This is assuming you have your org configured so sharing is only possible with existing external accounts.

The Solution

With an Office 365 subscription, you get a product called Microsoft Flow. Most people just equate this to being Microsoft’s attempt at creating an IFTT knock off. However, it’s so much more than that. You can, in essence, build out an entirely server-less API that you can interact with. It’s much closer to Azure Functions or Azure Logic Apps than IFTT.

In this blog post, I’m going to walk you through creating a Flow with an approval process to automatically add B2B users to your Azure AD tenant. Let’s go!

Initial Trigger

In order to make our Flow run, we want a trigger that provides it with input. There are many ways to do this, but I’m using a Microsoft Form (also part of O365). When the form is submitted, the flow will grab the values and do “stuff” with it.

First, go to the Forms app from https://portal.office.com (or just go to https://forms.office.com). Once there, click the button to create a new form. You can build your form to request whatever information you’d like. Here’s what I have and use in my Flow.



Now that the form is created, let's move over to Flow to get to the meat and taters. Buckle up, it’s a bit lengthy.

Flow Build

Head on over to https://portal.office.com again and find the Flow app. If you don’t see it, make sure you have the license assigned.

NOTE: For security reasons, you should create a new Flow Environment to create this (and future) Flows in. This will allow you to control who in your organization can access and run the Flow. Otherwise, everyone in your org can potentially be given access to view the Flow.

Create a new Automated – from Blank Flow



Now fill out the Flow name and choose the When a new response is submitted option as the initial trigger.



Once you have created the empty Flow, choose the Form that you created in the trigger.


Before we can continue, we need to hop over to Azure AD to register an App, grant it permissions and generate a secret (password) that we can use in our Flow for programmatically interacting with Azure AD.

Head over to https://portal.azure.com –> Azure Active Directory

Now go to App Registrations


Click on New Registration and fill out the details


Once created, save the Application ID and Tenant ID for later


Now we need to assign the App some permissions. It only needs enough access to perform the actions required. Step through the screenshots below to setup the App permissions.



Add API permissions to the app so it can interact programmatically.


Microsoft Graph is the API used for interacting with Azure AD


Use Application permissions so this can operate without user interaction.


Grant these permissions to the app. The User.Read is there by default.


Grant consent for all users (otherwise this won’t work)


Ok – Now that the permissions are squared away, lets create a secret that we can use to authenticate with. Go to Clients and Secrets in the app.

Create a new Client Secret



Give it a meaningful description and choose an expiration time that meets your requirements. Just know the Flow will break will the secret expires until you generate a new one and update the Flow. Make sure you copy the secret for use later (keep it somewhere secure like a password manager)



Ok – Now back to the Flow!

So far, all we’ve done in the Flow is add the Form trigger and choose our Form. Now we need to add all of our actions. The first thing we need to do is create three variables to store our Tenant ID, Application ID and Application Secret. These were all provided when we created our Registered App in Azure AD.

Add a new step and find the Initialize a Variable action. Now do that two more times. Fill in the values like shown below.


NOTE: To make your Flow easy to read, you should click on the ellipsis (…) on each item and rename it to something useful. Doing this after you’ve already created your Flow will break things so do it as you add items.

Now we want to get the response details from the submitted form. Add a Get response details action and choose the form and details to grab.





The next thing we’re going to do is authenticate to the Microsoft Graph API and get an access token. The access token will be used every time we make a call to the Microsoft Graph API

Add an action directly after the Get response details and choose HTTP



Make it look like the image above. We’re crafting an API call to the Microsoft Graph API to get an access token. I’ll include the body text below, just modify the variable names if you used something different.

client_id=@{variables('AppID')}&client_secret=@{variables('AppSecret')}&grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default

Now, we need to parse the JSON response that the Microsoft Graph API returns so we can use the values later. Add another action and choose the Parse JSON action. For the Content, choose the Body from the Get Access Token action. I’ve included the full Schema below as well.



{
    "properties": {
        "access_token": {
            "type": "string"
        },
        "expires_in": {
            "type": "integer"
        },
        "ext_expires_in": {
            "type": "integer"
        },
        "token_type": {
            "type": "string"
        }
    },
    "type": "object"
}

Now that we have our access token, we’re going to check to see if the user being requested already exists in the Azure AD tenant. If so, there is no need to proceed with requesting approvals. We’ll just notify the requester that the users already exist.

Add another HTTP action, this time we’re calling the Microsoft Graph API to check if an account with the same email address already exists. Make your HTTP action look like the one below.



Now we need to parse the response that is returned. Add another Parse JSON action and use the Body of the Check if User Exists action as the content. I included the schema below.



{
    "type": "object",
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "value": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "businessPhones": {
                        "type": "array"
                    },
                    "displayName": {
                        "type": "string"
                    },
                    "givenName": {},
                    "jobTitle": {},
                    "mail": {
                        "type": "string"
                    },
                    "mobilePhone": {},
                    "officeLocation": {},
                    "preferredLanguage": {},
                    "surname": {},
                    "userPrincipalName": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    }
                },
                "required": [
                    "businessPhones",
                    "displayName",
                    "givenName",
                    "jobTitle",
                    "mail",
                    "mobilePhone",
                    "officeLocation",
                    "preferredLanguage",
                    "surname",
                    "userPrincipalName",
                    "id"
                ]
            }
        }
    }
}

Let’s wrap things up

We now have an object (possibly blank) that we can use to determine if the user exists. We need to add a Condition Control to split our Flow depending on the result. Add an expression and use the definition below to check if the returned object is empty.

empty(body('Parse_User_Check')?['value'])

In the No branch, we’ll just send the requester a notification since the user already exists. I’m using the Office 365 Send an Email action to send emails.

In the Yes branch, we’ll kick off the rest of our Flow. In my approval process, I send the approval request to the requester’s manager. So, we need to request an object for their manager. Add another HTTP action.


Then of course, we need to parse the response so we can work with the values. Add another Parse JSON action, using the Body of the Get Requester’s Manager action.

{
    "type": "object",
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "@@odata.type": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "businessPhones": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "displayName": {
            "type": "string"
        },
        "givenName": {
            "type": "string"
        },
        "jobTitle": {},
        "mail": {
            "type": "string"
        },
        "mobilePhone": {},
        "officeLocation": {},
        "preferredLanguage": {
            "type": "string"
        },
        "surname": {
            "type": "string"
        },
        "userPrincipalName": {
            "type": "string"
        }
    }
}

Let’s go ahead and setup an approval condition. Add a Start and wait for an approval action. The mail value in Assigned to is parsed from the Manager object.

Next, let’s add another Condition Control to branch our Flow depending on the approval outcome. We’re checking if the Response value from the Get Manager Approval action is Approve.

In the NO branch, we’ll just send the requester an email saying they’ve been DENIED!

In the YES branch, we’ll finish off our Flow. We’re almost done! Add a new HTTP action. This will be an API call to the Microsoft Graph API to send an invite to the guest email address. Body content provided below, just replace the <> values with dynamic values like shown below. Make sure to also set the appropriate redirect URL.



{
  "inviteRedirectUrl": "https://www.yourdomain.tld",
  "invitedUserEmailAddress": "",
  "sendInvitationMessage": true,
  "invitedUserDisplayName": "",
  "invitedUserMessageInfo": {
    "customizedMessageBody": "Hello , You have been invited by  as a guest.  Continuing will allow you to collaborate and access data shared with you."
  }
}

Now, once again, let’s parse the invite response. Add a new Parse JSON action and use the body from the HTTP action above as the content.



{
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "id": {
            "type": "string"
        },
        "inviteRedeemUrl": {
            "type": "string"
        },
        "inviteRedirectUrl": {
            "type": "string"
        },
        "invitedUser": {
            "properties": {
                "id": {
                    "type": "string"
                }
            },
            "type": "object"
        },
        "invitedUserDisplayName": {},
        "invitedUserEmailAddress": {
            "type": "string"
        },
        "invitedUserMessageInfo": {
            "properties": {
                "ccRecipients": {
                    "items": {
                        "properties": {
                            "emailAddress": {
                                "properties": {
                                    "address": {},
                                    "name": {}
                                },
                                "type": "object"
                            }
                        },
                        "required": [
                            "emailAddress"
                        ],
                        "type": "object"
                    },
                    "type": "array"
                },
                "customizedMessageBody": {},
                "messageLanguage": {}
            },
            "type": "object"
        },
        "invitedUserType": {
            "type": "string"
        },
        "sendInvitationMessage": {
            "type": "boolean"
        },
        "status": {
            "type": "string"
        }
    },
    "type": "object"
}

Finally, we just need to send the requester an email letting them know that their request was approved and processed. We’ll use an Office 365 Send an Email action.

To test, fill out the form.



Your manager (or whoever you decide to assign the approval to) should get a request to approve.



If approved, you should see the new account show up in Azure AD as an “Invited User”.


Once the user completes the invitation, that will change to whatever account type that user has (such as Microsoft or Gmail).

Comments