Extending User Properties with Copilot Studio

Out of the box, Copilot Studio comes with several user properties you can utilize to personalize your audience’s experience (provided you are using authentication). These properties include:

  • User.DisplayName
  • User.Email
  • User.FirstName
  • User.Id
  • User.IsLoggedIn
  • User.Language
  • User.LastName
  • User.PrincipalName

These are quite helpful, but what if you’re looking to extend these properties to include things such as City, State, Country, and Department (or more)? If you’d like to add these properties, you’ve come to the right place! I spoke about extending user properties in the PnP Community Call on April 11, 2024, and so if you prefer to watch versus read, feel free to check out the recorded call.


The trick to extending what you know about the current user is doing it in an automated fashion, where you automatically retrieve these values from your system of choice, and don’t have to worry about explicitly injecting them into an existing topic and/or verifying their values haven’t been reset. In essence, what we want to do is guarantee that these values are always available for us to use, regardless of the state of the conversation. The great news is that a feature you’ve likely already seen when using a Question node is exactly what we’re going to use to do it:

Utilize ‘Variable gets value from this node if empty’ to ensure values are always available for a given variable

This feature, found by selecting your variable from the question node and clicking the “…” on the Reference panel, is labeled “Get value from this node if empty.” (if that option is already selected, which it is by default on question node, you’ll see “Don’t get value from this node if empty,” which is what you’d select if you wanted to undo this behavior.)

This feature is magic, as when you utilize it on a Global variable, any other topic that references that variable will always go to just this one node to get the value. It’s important to understand the distinction between this redirecting to just the node versus an entire topic; this will allow you to create a single “backend” topic for managing all of the global variables you wanted to use that needed default values, that your users would never interact with (extra credit: you can change the trigger of a topic to “Redirect,” where the topic is only accessible if another topic calls it, ensuring a user never stumbles upon it (see pic below for reference on changing a topic’s trigger)

Changing your topic’s trigger so that users can’t directly access it

The Details:

Now we have the core component for bringing in additional user properties, but in our case, we don’t want to ask the user for these values if we can simply load them from somewhere like their M365 User Profile. This is where it’s important to understand that the ‘Get value from this node if empty‘ is available on more than just question nodes. In fact, you’ll find this same behavior on all of the following nodes, when you click on their related Variable / Output property that you want to set a default value for:

  • Ask a question
  • Set variable value
  • Call an action

Set variable value is a great option when you have a default value that you want to set, where you don’t need to pull the value or ask the user for it; it could be a static value or something that uses a PowerFx expression.

Call an action allows us to call a Power Automate flow, and by tying one or more of its outputs to ‘Get value from this node if empty‘, Copilot Studio will automatically call your flow and populate the value(s), without ever needing to explicitly call the topic where this node lives or asking your user to perform an action.

How to Extend User Properties:

Now that we’ve explored how this feature that’s been available for quite some time actually extends beyond question nodes, we have everything we need to extend user properties.

  1. We’ll begin by adding a new topic called “Default Values” to our bot (feel free to change its trigger type to ‘Redirect’ if desired)
  2. Add a new “Call an action” node to the topic and select “Create a flow”:

  3. Inside your new flow’s pre-populated trigger action, “Run a flow from Copilot,” configure “UserEmail” as a ‘Text’ parameter:

  4. Add a “Get user profile (V2)” action to the flow, rename it to ‘GetUserProfile’, and set the User (UPN) to the UserEmail variable you added in the previous step:

  5. Add a “Compose” action to the flow, rename it to ‘GetUserObject’, and configure its ‘Inputs’ to return a JSON object that contains the values from ‘GetUserProfile’ that you’d like to include: (this is a simple example utilizing an out-of-the-box connector but you could call any API that you need and bring back the properties you’d like to utilize)

  6. In the existing “Respond to Copilot” node at the bottom of the flow, add a ‘UserObject’ output parameter of type ‘Text’ and use a PowerFx formula to set it’s value to the following formula (In my example I renamed my “Compose” action to “GetUserObject” – update code accordingly based on your name) The reason for casting this value to a string is that Copilot Studio is unable to handle raw JSON as an output value from Power Automate:

  7. Rename your flow by clicking its name in the top-left portion of the page and selecting ‘Save’

  8. Return to Copilot Studio, which should still be opened in a separate tab in your browser (A ‘Save and refresh’ window should appear asking you to click ‘Done’ once your work in Power Automate is complete)
  9. Go back to recreate and/or set your ‘Call an action’ node and select the Power Automate flow you just created
  10. Set the UserEmail input value to the System tab’s User.Email property(one of the standard user properties we can utilize)

  11. Click on the UserObject output value, change its usage to Global, click the “…” in the variable properties pane next to ‘Reference’, and click ‘Get value from this node if empty’

  12. Add a Set variable value node after the ‘Call an action’ node you just added, click on the box underneath ‘Set Variable’, click ‘Create a new variable’, call it UserInfo, and set it to be Global. Afterwards, follow the same step of clicking the “…” in the variable properties pane next to ‘Reference,’ and select ‘Get value from this node if empty’

  13. Click on the arrow on the right side of the box underneath the ‘To value‘ box, select the ‘Formula’ tab, enter the following formula, and click ‘Insert’:
    City: Text(ParseJSON(Global.UserObject).City),
    State: Text(ParseJSON(Global.UserObject).State),
    Country: Text(ParseJSON(Global.UserObject).Country),
    Department: Text(ParseJSON(Global.UserObject).Department)

  14. Save your topic now that you’ve got everything setup. The reason we added 2 nodes is because we’re only getting a string back from Power Automate, which isn’t conducive to working with the separate properties we’ve brought back. That’s where the second node comes into play; we convert that string from Power Automate into JSON, which sets the type of this variable to ‘Record’, where we’ll be able to see and select City, State, Country, and Department.
  15. Go to the system ‘Conversation Start’ topic, add a message node, and add some of the ‘User’ properties you’ve added inside your Global ‘UserInfo’ variable to see that these values automatically populate from Power Automate when you reference them, without ever explicitly calling the topic you created or asking the user for this information (when first testing you may be met with a message saying you need to ‘Connect’ to your flow; if so, select ‘Connect’ and follow the instructions to connect to the flow you just created)

That’s all there is to it! Now you’ve got more information about your user that you can use to communicate with them on messages and/or utilize for default values in other flows and actions without ever needing to explicitly request this information. (one thing I demoed on the PnP call was using this with Generative Answers to get the weather of the city the user is in, without asking them for a city)


Matt Jimison

Microsoft 365 Geek - Husband, father, lover of basketball, football, smoking / grilling, music, movies, video games, and craft beer!

Leave a Reply

Your email address will not be published. Required fields are marked *