New hires shouldn't wait hours for IT to manually set up their Microsoft 365 accounts. This hands-on lesson walks you through building a single Power Automate flow that creates user accounts, assigns licenses, and sends personalized welcome emails automatically — the moment HR submits a new hire request.

Picture this: it's 8:47 AM on a Monday, and your HR team has just hired three people who all start today. One of them — a new sales analyst — arrives at 9:00 AM, sits down at their desk, and... nothing works. Their Microsoft 365 account hasn't been created yet, their license hasn't been assigned, and their IT ticket is sitting in a queue behind seventeen others. By the time IT gets to it, half the morning is gone and your new hire's first impression of the company is a long wait and an apology.
This scenario plays out in organizations of every size, every week. User onboarding is fundamentally a multi-step coordination problem: someone enters data in HR software, someone else creates a Microsoft 365 account in the admin center, a third person assigns the right license, and hopefully someone remembers to send a welcome email. Each handoff is a delay, a potential mistake, and an unnecessary burden on your IT team. The good news is that every single one of these steps is automatable — and Power Automate gives you the tools to wire them together into a single, reliable flow.
By the end of this lesson, you'll have a working Power Automate flow that creates a new Microsoft 365 user account, assigns a license, and delivers a personalized welcome email — all triggered automatically the moment a new hire record is submitted. No manual IT tickets required.
What you'll learn:
You should have a basic familiarity with the Power Automate interface — knowing how to open the designer and add actions will save you time. If you haven't done that yet, get comfortable with getting started with the Power Automate interface before working through this lesson. You'll also need a Microsoft 365 account with sufficient admin permissions to create users and assign licenses — typically Global Admin or User Administrator plus License Administrator roles in your tenant.
Before touching a single action in Power Automate, it's worth mapping out what you're actually automating. User onboarding isn't one task — it's a sequence of dependent tasks, and that ordering matters.
Here's the logical chain:
Notice that step 3 depends on step 2. You cannot assign a license to an account that doesn't exist yet. And step 4 ideally only runs if steps 2 and 3 both succeeded — otherwise you're welcoming someone to an account that doesn't actually work. This is why building this as a single flow, with intentional sequencing, is so much more reliable than having separate processes that don't talk to each other.
Key insight: Automation isn't just about speed — it's about enforcing the correct sequence every single time. A human following a checklist might skip step 3 when they're rushed. Your flow never will.
Your flow needs a starting point — a way to receive new hire data that triggers the automation. For this lesson, we'll use Microsoft Forms because it's accessible to non-technical HR staff, integrates natively with Power Automate, and requires zero infrastructure to set up.
Go to forms.microsoft.com and create a new form called "New Hire Onboarding Request." Add the following fields:
The license type field is important. Different roles need different license tiers — a frontline worker might only need Business Basic, while a knowledge worker doing data analysis needs at minimum Business Standard. Capturing this at request time means your flow can act on it instead of defaulting every new hire to the most expensive option.
Tip: If your organization already captures new hire data in a SharePoint list or an HR system like Workday, you can swap out the Forms trigger for a SharePoint "When an item is created" trigger or an HTTP webhook trigger. The account creation and license assignment steps work identically regardless of where the data comes from. Learn more about Power Automate triggers and when to use them.
Open Power Automate at make.powerautomate.com. Click "Create" in the left navigation, then choose "Automated cloud flow." Give your flow a descriptive name — something like "M365 User Onboarding - Provision and Welcome." When prompted to choose a trigger, search for "Microsoft Forms" and select "When a new response is submitted." Click "Create."
In the trigger action that appears, click the "Form Id" dropdown and select the "New Hire Onboarding Request" form you created.
Next, you need to actually retrieve the form response content — the trigger only tells you that a response was submitted, not what was in it. Click the "New step" button, search for "Microsoft Forms," and select the "Get response details" action. Set "Form Id" to your form, and set "Response Id" to the "Response Id" dynamic value from the trigger. Now all the form fields are available as dynamic content throughout the rest of your flow.
Microsoft 365 usernames follow the format firstname.lastname@yourdomain.com. You need to construct this string from the separate first and last name fields before you can create the account.
Add a "Compose" action (search for "Compose" under Data Operation). In the Inputs field, enter the following expression:
concat(toLower(outputs('Get_response_details')?['body/rf4c7a8b2b9e449de9a5bfe014f8c1234']), '.', toLower(outputs('Get_response_details')?['body/rf9d2e3c1a4f8440be8b7fce015a2b5678']), '@contoso.com')
In practice, Power Automate makes this much easier — instead of typing that raw expression, you'll click into the Inputs field, click "Expression," and build the concat and toLower calls by inserting dynamic content tokens for "First Name" and "Last Name." Your finished expression should produce something like sarah.chen@contoso.com from inputs of "Sarah" and "Chen."
Name this Compose action "Compose Username" by clicking the action title.
Warning: Username collisions happen. If you already have a
john.smith@contoso.comand hire another John Smith, the account creation step will fail. For production flows, add a step that calls the "Get user" action first and checks whether the account already exists before attempting to create it. For this foundation lesson we'll focus on the happy path, but collision handling is essential before deploying to real HR processes.
New accounts need an initial password. You have two reasonable options: use a static temporary password that IT communicates separately, or generate a dynamic one. For simplicity and security, we'll compose a temporary password using a formula that combines a fixed prefix with part of the user's last name and a number, then force a password change on first login.
Add another "Compose" action and name it "Compose Temp Password." In the Inputs, build an expression like:
concat('Welcome@', toUpper(first(split(triggerBody()?['...LastName'], ''))), '2024!')
Or more practically: use the expression editor to write something like concat('Welcome@', outputs('Get_response_details')?['body/LastName'], '24!') which would produce Welcome@Chen24! for Sarah Chen. This meets typical complexity requirements (uppercase, lowercase, number, special character).
Note: In a production environment, you should store secrets and sensitive values — including generated passwords — in Azure Key Vault rather than in the flow itself or in email bodies. See integrating Power Automate with Azure Key Vault and Managed Identities to learn how to handle credentials securely.
This is the core of the flow. Add a new step, search for "Microsoft 365 Users," and select the "Create user" action. You'll be prompted to sign in with an account that has User Administrator permissions — use a dedicated service account rather than your personal admin account for anything going to production.
Fill in the action fields as follows:
Sarah Chenconcat(toLower(...), '.', toLower(...)) pattern without the domain: sarah.chenThe "Create user" action, when successful, returns a user object containing the new account's unique object ID (id property). You'll need this ID in the next step to assign the license, so it's important that this step runs successfully before moving on.
Here's where things get slightly more technical. The Microsoft 365 Users connector has a "Assign license to user" action, but to use it, you need to know the SKU ID of the license you want to assign. SKU IDs are GUIDs that Microsoft uses internally to identify license types, and they're the same across all tenants.
Common SKU IDs you'll use:
cfq7ttc0k59j (this is a product family ID; the actual license assignment uses a GUID)cfq7ttc0k59j:0002The most reliable way to get exact SKU IDs for your tenant is to run a quick check in the Microsoft 365 Admin Center under Billing → Your Products, or to make a Graph API call. For this flow, we'll use a workaround: add a "Condition" action that checks which license type the HR form selected, then branches to different "Assign license" actions, each hardcoded with the appropriate SKU ID for that branch.
Add a new step and choose the "Condition" action. Set it up to check if the "License Type Needed" form response equals "Microsoft 365 Business Standard." In the "If yes" branch, add a "HTTP" action (or use the Microsoft 365 Users "Assign license" action if it's available in your connector version) and in the "If no" branch, handle the Business Basic case.
For the actual license assignment, the most reliable method at the time of writing is an HTTP action that calls the Microsoft Graph API directly:
Method: POST
URI: https://graph.microsoft.com/v1.0/users/{userId}/assignLicense
Headers:
Content-Type: application/json
Body:
{
"addLicenses": [
{
"skuId": "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"
}
],
"removeLicenses": []
}
Replace {userId} with the id property from the "Create user" action's output, and replace the skuId with the correct GUID for your chosen license. Using Graph API HTTP actions directly is a powerful pattern — you can learn the full approach in advanced Power Automate HTTP actions for production integration.
Tip: You'll need to configure authentication on the HTTP action. Set Authentication to "Active Directory OAuth" and provide your tenant ID, client ID, and client secret from an Azure AD app registration that has the
User.ReadWrite.AllandOrganization.Read.AllGraph API permissions. This sounds complex but takes about 10 minutes to set up in the Azure portal — and it's a one-time configuration.
With the account created and licensed, the final step is notifying the new hire. Add a new step, search for "Office 365 Outlook," and select "Send an email (V2)."
Configure it as follows:
Welcome to Contoso, [First Name]! Your Microsoft 365 account is ready.Here's a solid welcome email body you can adapt:
<p>Hi [First Name],</p>
<p>Welcome to Contoso! We're thrilled to have you joining the [Department] team.
Your Microsoft 365 account has been set up and is ready to use.</p>
<p><strong>Your sign-in details:</strong></p>
<ul>
<li><strong>Email / Username:</strong> [Username from Compose step]</li>
<li><strong>Temporary Password:</strong> [Temp Password from Compose step]</li>
</ul>
<p>When you first sign in at <a href="https://portal.office.com">portal.office.com</a>,
you'll be prompted to change your password. Please choose something memorable
and keep it secure.</p>
<p>If you have any questions before your start date of [Start Date],
reach out to IT support at it@contoso.com.</p>
<p>We can't wait to work with you!</p>
<p>The Contoso IT Team</p>
Insert the dynamic content tokens from your form response and Compose actions into the appropriate placeholders.
Sending automated, personalized emails like this is one of Power Automate's most immediately useful capabilities. If you want to build more sophisticated email automations, automating email notifications with Power Automate covers additional patterns and formatting techniques worth knowing.
Your flow will eventually fail. Maybe the username is already taken. Maybe the Graph API authentication token expires. Maybe the Forms service has a hiccup. Without error handling, you'll have no idea it happened until someone's new hire is sitting at an empty desk.
The minimum viable error handling for this flow involves two things:
1. Configure each critical action to run on failure. Select the "Create user" action, click the three-dot menu, and choose "Configure run after." This lets you add a parallel branch that runs only when the create user action fails — use it to send an alert email to IT with the error details.
2. Check the status code from the license assignment HTTP action. After the HTTP action, add a Condition that evaluates whether outputs('Assign_License')?['statusCode'] equals 200. If it doesn't, send a failure notification with the response body included so IT knows exactly what went wrong.
For a comprehensive look at building resilient flows that handle failures gracefully, see master error handling and retry patterns in Power Automate.
Warning: Never send the temporary password in plain text via email in a production environment without understanding your organization's security policies. In many regulated industries, credentials must be delivered through a separate channel (like SMS or a secure portal). The email approach in this lesson is suitable for learning and low-risk internal environments.
Now it's your turn to build this flow from scratch in a test environment. Here's a structured exercise:
Setup (10 minutes):
Build the flow (30-40 minutes):
Test the flow (10 minutes):
Clean up: Delete the test user account from the Microsoft 365 admin center to reclaim the license.
"Create user" action fails with "Authorization_RequestDenied" Your Power Automate connection doesn't have sufficient permissions. Make sure you're connected with an account that has at minimum the User Administrator role in Microsoft 365. If you're using Graph API HTTP calls, verify your app registration has been granted admin consent for the required permissions.
License assignment fails immediately after account creation This is a timing issue. Microsoft 365 occasionally takes a few seconds to fully propagate a new account before it's ready to accept license assignments. Add a "Delay" action between account creation and license assignment — even a 15-30 second delay resolves this in most cases. Find it by searching "Delay" in the action picker.
Welcome email arrives but fields show as blank or show the raw expression text You've either referenced the wrong dynamic content token (e.g., the trigger's Response Id instead of the "Get response details" action's field values) or there's a typo in your expression. Click on each field in the email body and verify it shows the correct dynamic content chip — it should display the field name, not a raw formula.
The flow runs but the "Get response details" action shows "BadRequest" The Form Id in the trigger and the Form Id in "Get response details" don't match. Make sure both reference the same form, and that the Form Id dropdown is populated from the correct connection. Refresh the connection if needed.
Username contains spaces or special characters
If someone enters "Mary-Jane" as a first name, your concat expression will produce mary-jane.watson@contoso.com which is actually valid — hyphens are allowed in UPNs. But spaces are not. Add a replace() expression wrapping the name fields: replace(toLower(outputs('Get_response_details')?['body/FirstName']), ' ', '') to strip any spaces before composing the username.
Tip: Use the Power Automate Run History and Flow Checker tools to inspect exactly which action failed and what the input/output values were at the time of failure. This is the fastest path to diagnosing any issue in a complex flow.
You've now built a complete user onboarding automation that handles the three most time-consuming IT provisioning tasks — account creation, license assignment, and welcome communication — inside a single, reliable flow. Every time HR submits a new hire request, the entire sequence runs automatically, in the correct order, in under two minutes.
Here's what the flow accomplishes that manual processes can't consistently deliver: it eliminates the human coordination overhead between HR and IT, enforces proper sequencing every time, and creates an audit trail of every provisioning action through Power Automate's run history.
Where to take this next:
The flow you've built is a strong foundation, but real enterprise onboarding has more moving parts. Consider these extensions:
The most powerful thing about building this automation isn't the time it saves today — it's that once it exists, onboarding a new person costs your team essentially zero incremental effort, no matter how many people you hire.