From f92a2caeca190281c3c895a0b74dcad7c7acf22c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 14:26:11 +0000 Subject: [PATCH 01/20] chore: rebuild catalog [skip ci] --- resource-stats.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resource-stats.json b/resource-stats.json index 9cc7c168..ce349318 100644 --- a/resource-stats.json +++ b/resource-stats.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-07-27T19:00:31.864Z", + "generatedAt": "2026-07-30T14:26:11.041Z", "sources": { "views": "clarity", "upvotes": "github-discussions" @@ -12,7 +12,7 @@ "ai-council": { "views": 78, "viewsUniques": 61, - "upvotes": 0, + "upvotes": 1, "discussion": { "number": 501, "url": "https://github.com/microsoft/FastTrack/discussions/501" From bdf71cc7857541ffa5dc2dcbc93c71e3fcc56ff3 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Thu, 30 Jul 2026 10:33:51 -0400 Subject: [PATCH 02/20] feat(ess): add ServiceNow ITSM Get Request Details sample Adds a new ESS extension sample that lets employees look up ServiceNow service catalog requests (REQ numbers) from the ESS agent. Components: - User-facing topic with AI input extraction and adaptive card display - System routing topic with InvokeFlowAction wiring - Standalone Power Automate flow querying sc_request table via ServiceNow connector - README with setup instructions for both ESS Maker Kit and manual deployment The built-in ESS ITSM shared flow only supports incident (INC) scenarios. This sample uses a standalone flow to query the sc_request table directly, following the ESS two-topic pattern for consistency with out-of-the-box topics. --- .../README.md | 94 +++++++ ...ow-itsm-system-get-request-details.mcs.yml | 40 +++ ...ervicenow-itsm-get-request-details.mcs.yml | 245 +++++++++++++++++ .../workflow/metadata.yml | 16 ++ .../workflow/workflow.json | 256 ++++++++++++++++++ 5 files changed, 651 insertions(+) create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md new file mode 100644 index 00000000..30df4ceb --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -0,0 +1,94 @@ +--- +title: ESS ServiceNow ITSM โ€“ Get Request Details +type: extension +category: Copilot Studio / Employee Self-Service +summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees look up the status and details of a ServiceNow service catalog request (REQ number) via natural language. +author: + - Dean Cron +version: 1.0.0 +published: "2026-07-30" +tags: + - ESS + - Employee Self-Service + - ServiceNow + - ITSM + - service catalog + - sc_request +format: extension +whatItIs: >- + A two-topic + standalone flow extension for the Microsoft Employee Self-Service + (ESS) Copilot Studio agent. Employees can ask the agent about any service + catalog request (REQ prefix) and receive a rich adaptive card showing the + request number, state, description, who it's assigned to, and a direct link + to ServiceNow. +whyUseIt: + - The ESS shared ITSM orchestrator flow only supports incident (INC) lookups. + This extension adds sc_request (REQ) support without modifying any shared flows. + - Follows the ESS two-topic pattern (user-facing + system routing) for + consistency with out-of-the-box ESS topics. + - Uses the same msdyn_ServiceNowOutputFieldMapperPlugin for field renaming, + keeping the response shape compatible with standard ESS adaptive card patterns. +howToUse: |- + See the Setup section below. +prerequisites: + - Microsoft Employee Self-Service agent (deployed) + - ServiceNow ITSM extension pack installed in Copilot Studio + - An active ServiceNow connection in Power Platform (Entra ID auth recommended) + - ESS Maker Kit (for automated deployment via push.py) +--- + +# ESS ServiceNow ITSM โ€“ Get Request Details + +## ๐Ÿ“Œ Overview + +Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog requests** (REQ numbers) from ServiceNow. The built-in ESS ITSM orchestrator only handles incidents (INC). This sample adds a standalone flow that queries the `sc_request` table directly. + +**Example conversation:** + +> User: "What's the status of my request REQ0012345?" +> Agent: *[Adaptive card showing request number, state, description, assigned to, last updated, and a link to open in ServiceNow]* + +--- + +## ๐Ÿ“ Files + +| File | Purpose | +|------|---------| +| `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, adaptive card | +| `topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” routes to the standalone flow via `InvokeFlowAction` | +| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_request` by SysId | +| `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | + +--- + +## ๐Ÿš€ Setup + +### Option A โ€” ESS Maker Kit (recommended) + +1. Open your ESS Maker Kit workspace and run `/setup` to connect to the target environment. +2. Copy the `topics/` files into `workspace/agents/{your-agent-slug}/topics/`. +3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{NEW-GUID}/`. +4. In **both topic files**, replace `{AGENT_SCHEMA_NAME}` with your agent's schema name (e.g. `msdyn_copilotforemployeeselfservicehr`). +5. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. +6. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). +7. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. +8. Run `python scripts/push.py` to deploy. +9. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector with `runtimeSource: invoker`. +10. Run `python scripts/push.py --repair "Get Request Details"` to wire the topicโ†’flow link. +11. Publish the agent in Copilot Studio. + +### Option B โ€” Manual + +1. In Power Automate, import `workflow/workflow.json` as a new cloud flow. +2. Update the ServiceNow and Dataverse connection references to point to your environment's connections. +3. Turn the flow on. Note the flow's ID (visible in the Power Automate URL). +4. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` as the content โ€” replacing `{AGENT_SCHEMA_NAME}` with your schema name and `{FLOW_GUID}` with the flow ID from step 3. +5. Publish. + +--- + +## โš ๏ธ Known Limitations + +- **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flow is created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 9 above). +- **sc_request fields** โ€” the flow returns `request_state` (not `state`) and `requested_for` (not `caller_id`). The adaptive card handles this, but if you customise the output be aware the field names differ from the incident table. +- **SysId lookup only** โ€” the flow looks up by SysId. The user-facing topic extracts the REQ number or SysId from the user's message. If only a REQ number is provided (not a SysId), ensure your ServiceNow instance returns records when queried by `number` field โ€” you may need to adapt the flow to use a `GetRecords` filter instead of `GetRecord`. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml new file mode 100644 index 00000000..37b61457 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml @@ -0,0 +1,40 @@ +kind: AdaptiveDialog +inputs: + - kind: ManualTaskInput + propertyName: SysId + value: ="" + +beginDialog: + kind: OnRedirect + id: main + actions: + - kind: InvokeFlowAction + id: callGetRequestDetailsFlow + input: + binding: + text: =Topic.SysId + + output: + binding: + errormessage: Topic.ErrorMessage + issuccessful: Topic.IsSuccessful + servicenowdata: Topic.ServiceNowData + servicenowsessionid: Topic.ServiceNowSessionID + servicenowtransactionid: Topic.ServiceNowTransactionID + statuscode: Topic.StatusCode + + # Replace {FLOW_GUID} with the workflowId from workflow/metadata.yml + flowId: "{FLOW_GUID}" + +inputType: + properties: + SysId: + displayName: SysId + type: String + +outputType: + properties: + ServiceNowData: + displayName: ServiceNowData + description: Request details data returned from ServiceNow. + type: String diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml new file mode 100644 index 00000000..a6590af5 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml @@ -0,0 +1,245 @@ +kind: AdaptiveDialog +inputs: + - kind: ManualTaskInput + propertyName: IconBase64 + value: data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPG1hc2sgaWQ9Im1hc2swXzM5NzNfMTkwMTU4IiBzdHlsZT0ibWFzay10eXBlOmx1bWluYW5jZSIgbWFza1VuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIj4KPHBhdGggZD0iTTYgNEM0Ljg5NTQzIDQgNCA0Ljg5NTQzIDQgNlYxNEM0IDE1LjEwNDYgNC44OTU0MyAxNiA2IDE2SDE0QzE1LjEwNDYgMTYgMTYgMTUuMTA0NiAxNiAxNFYxMS41QzE2IDExLjIyMzkgMTYuMjIzOSAxMSAxNi41IDExQzE2Ljc3NjEgMTEgMTcgMTEuMjIzOSAxNyAxMS41VjE0QzE3IDE1LjY1NjkgMTUuNjU2OSAxNyAxNCAxN0g2QzQuMzQzMTUgMTcgMyAxNS42NTY5IDMgMTRWNkMzIDQuMzQzMTUgNC4zNDMxNSAzIDYgM0g4LjVDOC43NzYxNCAzIDkgMy4yMjM4NiA5IDMuNUM5IDMuNzc2MTQgOC43NzYxNCA0IDguNSA0SDZaTTExIDMuNUMxMSAzLjIyMzg2IDExLjIyMzkgMyAxMS41IDNIMTYuNUMxNi43NzYxIDMgMTcgMy4yMjM4NiAxNyAzLjVWOC41QzE3IDguNzc2MTQgMTYuNzc2MSA5IDE2LjUgOUMxNi4yMjM5IDkgMTYgOC43NzYxNCAxNiA4LjVWNC43MDcxMUwxMS44NTM2IDguODUzNTVDMTEuNjU4MyA5LjA0ODgyIDExLjM0MTcgOS4wNDg4MiAxMS4xNDY0IDguODUzNTVDMTAuOTUxMiA4LjY1ODI5IDEwLjk1MTIgOC4zNDE3MSAxMS4xNDY0IDguMTQ2NDVMMTUuMjkyOSA0SDExLjVDMTEuMjIzOSA0IDExIDMuNzc2MTQgMTEgMy41WiIgZmlsbD0id2hpdGUiLz4KPC9tYXNrPgo8ZyBtYXNrPSJ1cmwoI21hc2swXzM5NzNfMTkwMTU4KSI+CjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzQ2NEZFQiIvPgo8L2c+Cjwvc3ZnPgo= + + - kind: AutomaticTaskInput + propertyName: RequestIdentifier + description: |- + `RequestIdentifier` is a unique value used to identify a service catalog request. + + There are only two valid types of RequestIdentifiers: + 1. **SysID** + - A globally unique 32-character identifier. + - Format: hexadecimal string (digits 0-9 and lowercase letters a-f). + - Regex: `^[a-f0-9]{32}$` + 2. **RequestNumber** + - A unique identifier between 8-14 characters. + - Format: Always starts with the prefix `REQ` followed by digits. + - Regex: `^REQ\d{5,}$` + + If neither identifier is available, ask the user to provide their REQ number. + + **Important:** The extracted `SysID` or `RequestNumber` must be an exact match with the user query. Partial or fuzzy matches are not valid. + entity: StringPrebuiltEntity + shouldPromptUser: false + + - kind: AutomaticTaskInput + propertyName: RelevantRequestFound + description: |- + `RelevantRequestFound` is a boolean value that indicates whether a matching request was found based on the user's input. + + Set `RelevantRequestFound = true` **only if** one of the following conditions is met: + 1. **Exact Identifier Match** + - The user provides a `RequestNumber` (8-14 characters, starts with `"REQ"`) or a `SysID` (32-character hexadecimal string: digits `0-9` and lowercase letters `a-f`). + + 2. **High-Relevance Descriptive Match** + - The user describes a specific request with VERY HIGH SEMANTIC RELEVANCE. + - If the match is partial, uncertain, or low-confidence, set `RelevantRequestFound = false`. + + **Do not set `RelevantRequestFound = true` for partial, fuzzy, or ambiguous matches.** + entity: BooleanPrebuiltEntity + shouldPromptUser: false + +modelDescription: |- + Use this tool when a user asks for details about a specific service catalog request. Requests always start with the prefix REQ (e.g. REQ0012345). + + This tool retrieves details for a specific service catalog request identified by its REQ number or SysId. + + Do NOT use this tool for incident tickets (which start with INC). Use ServiceNow-ITSM-Get-Ticket-Details for incidents instead. + + Example queries handled by this tool: + * "What's the status of my request REQ0012345?" + * "Show me details of REQ0067890" + * "Where is my service request?" + * "Can you look up request REQ0011223 for me?" + * "Details on my catalog request" + * "Has my software request been approved?" + * "What's happening with my hardware request?" + * "Check the status of my access request" +beginDialog: + kind: OnRecognizedIntent + id: main + intent: {} + actions: + - kind: ConditionGroup + id: conditionGroup_checkRequest + conditions: + - id: conditionItem_requestFound + condition: =Topic.RelevantRequestFound = true + displayName: Is a relevant request found? + actions: + - kind: BeginDialog + id: callSystemGetRequestDetails + input: + binding: + SysId: =Topic.RequestIdentifier + + dialog: {AGENT_SCHEMA_NAME}.topic.ess-hr-servicenow-itsm-system-get-request-details + output: + binding: + ServiceNowData: Topic.ServiceNowData + + - kind: ConditionGroup + id: conditionGroup_dataFound + conditions: + - id: conditionItem_hasData + condition: =!IsBlank(Topic.ServiceNowData) + displayName: Was request data returned? + actions: + - kind: ParseValue + id: parseRequestDetails + variable: Topic.RequestDetails + valueType: + kind: Record + properties: + AssignedTo: + type: String + examples: + + Comments: + type: String + examples: + + Description: + type: String + examples: + + LastUpdated: + type: String + examples: + + RequestedFor: + type: String + examples: + + RequestNumber: + type: String + examples: + + ServiceNowPortalBaseURI: + type: String + examples: + + ShortDescription: + type: String + examples: + + State: + type: String + examples: + + SysId: + type: String + examples: + + value: =Topic.ServiceNowData + + - kind: SetVariable + id: setFirstComment + variable: Topic.FirstComment + value: =First(Split(Topic.RequestDetails.Comments, Char(10) & Char(10))).Value + + - kind: SetTextVariable + id: setRequestTitle + variable: Topic.RequestTitle + value: "Service Request #{Topic.RequestDetails.RequestNumber} | " + + - kind: SendActivity + id: sendRequestCard + activity: + text: + - You can view the request details below. + attachments: + - kind: AdaptiveCardTemplate + cardContent: |- + ={ + type: "AdaptiveCard", + '$schema': "http://adaptivecards.io/schemas/adaptive-card.json", + version: "1.3", + body: [ + { + type: "Container", + items: [ + { + type: "RichTextBlock", + inlines: [ + {type: "TextRun", text: Topic.RequestTitle, weight: "Bolder"}, + {type: "TextRun", text: Topic.RequestDetails.State, color: If(Lower(Topic.RequestDetails.State) = "approved", "good", "default"), weight: "Bolder"} + ] + }, + {type: "TextBlock", text: Topic.RequestDetails.ShortDescription, wrap: true, weight: "Bolder", size: "Medium"}, + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "50", items: [{type: "TextBlock", text: "Requested for", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.RequestedFor), "โ€”", Topic.RequestDetails.RequestedFor), wrap: true, spacing: "None"}]}, + {type: "Column", width: "50", items: [{type: "TextBlock", text: "Assigned to", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.AssignedTo), "No agent assigned", Topic.RequestDetails.AssignedTo), wrap: true, spacing: "None"}]} + ] + }, + {type: "ColumnSet", columns: [{type: "Column", width: "100", items: [{type: "TextBlock", text: "Last updated", wrap: true, isSubtle: true}, {type: "TextBlock", text: Text(DateValue(Topic.RequestDetails.LastUpdated), "mmmm d, yyyy"), wrap: true, spacing: "None"}]}]}, + If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: "Description", wrap: true, isSubtle: true}), + If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: Topic.RequestDetails.Description, wrap: true, spacing: "None"}), + If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: "Latest comment", wrap: true, isSubtle: true}), + If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: Mid(Topic.FirstComment, Find(Char(10), Topic.FirstComment) + 1, Len(Topic.FirstComment)), wrap: true, spacing: "None"}), + {type: "TextBlock", text: "SysId", isVisible: false}, + {type: "TextBlock", text: Topic.RequestDetails.SysId, isVisible: false} + ] + }, + { + type: "Container", + separator: true, + spacing: "ExtraLarge", + items: [ + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "auto", items: [{type: "TextBlock", text: "[Open in ServiceNow](" & Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId & ")", wrap: true, weight: "Bolder"}]}, + {type: "Column", width: "auto", selectAction: {type: "Action.OpenUrl", url: Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId}, items: [{type: "Image", url: Topic.IconBase64}]} + ] + } + ] + } + ] + } + + - kind: CancelAllDialogs + id: cancelAllDialogs + + elseActions: + - kind: SendActivity + id: sendNotFound + activity: + text: + - I couldn't find a service request matching that number. Please check the REQ number and try again. + + elseActions: + - kind: SetVariable + id: setNotFound + variable: Topic.RelevantRequestFound + value: false + +inputType: + properties: + IconBase64: + displayName: IconBase64 + type: String + examples: + + RelevantRequestFound: + displayName: RelevantRequestFound + type: Boolean + examples: + + RequestIdentifier: + displayName: RequestIdentifier + description: REQ number or SysId of the service catalog request. + type: String + examples: + +outputType: + properties: + RelevantRequestFound: + displayName: RelevantRequestFound + type: Boolean + examples: diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml new file mode 100644 index 00000000..5649d6fc --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml @@ -0,0 +1,16 @@ +# Replace {FLOW_GUID} with a newly generated GUID (e.g. from: python -c "import uuid; print(uuid.uuid4())") +# Replace {SERVICENOW_CONNREF} with your agent's ServiceNow connection reference logical name +# Replace {DATAVERSE_CONNREF} with your agent's Dataverse connection reference logical name +# Both connrefs can be found in workspace/agents/{slug}/connectionreferences.mcs.yml +jsonFileName: workflow/workflow.json +workflowId: "{FLOW_GUID}" +name: ESS HR ServiceNow ITSM Get Request Details +type: 1 +description: "Retrieves details for a specific ServiceNow service catalog request (sc_request table) by SysId." +subprocess: false +category: 5 +mode: 0 +scope: 4 +stateCode: 1 +statusCode: 2 +isTransacted: true diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json new file mode 100644 index 00000000..2c3bfb8c --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json @@ -0,0 +1,256 @@ +{ + "schemaVersion": "1.0.0.0", + "properties": { + "connectionReferences": { + "shared_service-now": { + "api": { + "name": "shared_service-now" + }, + "connection": { + "connectionReferenceLogicalName": "{SERVICENOW_CONNREF}" + }, + "runtimeSource": "invoker" + }, + "shared_commondataserviceforapps": { + "api": { + "name": "shared_commondataserviceforapps" + }, + "connection": { + "connectionReferenceLogicalName": "{DATAVERSE_CONNREF}" + }, + "runtimeSource": "embedded" + } + }, + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$authentication": { + "defaultValue": {}, + "type": "SecureObject" + }, + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "text": { + "description": "System Identifier (SysId) of the service catalog request to retrieve.", + "title": "SysId", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + } + }, + "required": ["text"] + } + } + } + }, + "actions": { + "Initialize_variable_-_ResponseBody": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ResponseBody", + "type": "object" + } + ] + }, + "runAfter": {} + }, + "Retrieve_Base_ITSM_Config": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "entityName": "msdyn_employeeselfservicetemplateconfigs", + "$select": "msdyn_value", + "$filter": "msdyn_uniquename eq 'msdyn_ServiceNowITSM'", + "$top": 1 + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "ListRecords", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": { + "Initialize_variable_-_ResponseBody": ["Succeeded"] + } + }, + "ServiceNow_Get_Request": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "tableType": "sc_request", + "sysid": "@triggerBody()?['text']", + "sysparm_display_value": true, + "sysparm_exclude_reference_link": false, + "sysparm_fields": "number,short_description,description,request_state,requested_for,assigned_to,comments,sys_updated_on,sys_id" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_service-now", + "operationId": "GetRecord", + "connectionName": "shared_service-now" + } + }, + "runAfter": { + "Retrieve_Base_ITSM_Config": ["Succeeded"] + }, + "limit": { + "timeout": "PT1M" + } + }, + "Condition_-_ServiceNow_call_succeeded": { + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@outputs('ServiceNow_Get_Request')?['statusCode']", + 200 + ] + }, + { + "less": [ + "@outputs('ServiceNow_Get_Request')?['statusCode']", + 300 + ] + } + ] + }, + "actions": { + "Map_output_fields": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", + "item/ServiceNowTableData": "@body('ServiceNow_Get_Request')?['result']", + "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"description\",\"OutputName\":\"Description\"},{\"FieldName\":\"request_state\",\"OutputName\":\"State\"},{\"FieldName\":\"requested_for\",\"OutputName\":\"RequestedFor\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"comments\",\"OutputName\":\"Comments\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"}]", + "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "PerformUnboundAction", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": {} + }, + "Set_success_response": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { + "ServiceNowData": "@outputs('Map_output_fields')?['body/ServiceNowMappedOutput']", + "ErrorMessage": "", + "StatusCode": "@outputs('ServiceNow_Get_Request')?['statusCode']", + "IsSuccessful": "@true", + "ServiceNowSessionID": "@if(contains(outputs('ServiceNow_Get_Request')?['headers/Set-Cookie'], 'glide_session_store'), first(split(last(split(outputs('ServiceNow_Get_Request')?['headers/Set-Cookie'], 'glide_session_store=')), ';')), '')", + "ServiceNowTransactionID": "@outputs('ServiceNow_Get_Request')?['headers/X-Transaction-ID']" + } + }, + "runAfter": { + "Map_output_fields": ["Succeeded"] + } + } + }, + "else": { + "actions": { + "Set_failure_response": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { + "ServiceNowData": "", + "ErrorMessage": "@{outputs('ServiceNow_Get_Request')?['body/error/message']}", + "StatusCode": "@outputs('ServiceNow_Get_Request')?['statusCode']", + "IsSuccessful": "@false", + "ServiceNowSessionID": "", + "ServiceNowTransactionID": "" + } + }, + "runAfter": {} + } + } + }, + "runAfter": { + "ServiceNow_Get_Request": ["Succeeded", "Failed", "TimedOut", "Skipped"] + } + }, + "Respond_to_Copilot": { + "type": "Response", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "servicenowdata": { + "title": "ServiceNowData", + "x-ms-dynamically-added": true, + "type": "string", + "description": "Data returned from ServiceNow." + }, + "issuccessful": { + "title": "IsSuccessful", + "x-ms-dynamically-added": true, + "type": "boolean", + "description": "Boolean to indicate whether the flow succeeded or not." + }, + "errormessage": { + "title": "ErrorMessage", + "x-ms-dynamically-added": true, + "type": "string", + "description": "Error message in case of a failure." + }, + "statuscode": { + "title": "StatusCode", + "x-ms-dynamically-added": true, + "type": "number", + "description": "Integer value to indicate the request status code." + }, + "servicenowsessionid": { + "title": "ServiceNowSessionID", + "description": "The glide_session_store cookie.", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + }, + "servicenowtransactionid": { + "title": "ServiceNowTransactionID", + "description": "The X-Transaction-ID header.", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + } + }, + "additionalProperties": {} + }, + "statusCode": 200, + "body": { + "servicenowdata": "@{variables('ResponseBody')?['ServiceNowData']}", + "issuccessful": "@variables('ResponseBody')?['IsSuccessful']", + "errormessage": "@{variables('ResponseBody')?['ErrorMessage']}", + "statuscode": "@variables('ResponseBody')?['StatusCode']", + "servicenowsessionid": "@{variables('ResponseBody')?['ServiceNowSessionID']}", + "servicenowtransactionid": "@{variables('ResponseBody')?['ServiceNowTransactionID']}" + } + }, + "runAfter": { + "Condition_-_ServiceNow_call_succeeded": ["Succeeded"] + } + } + } + } + } +} + From 78cd5e8cacdcd87a2cbdfc0d62f4f5058427a000 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Thu, 30 Jul 2026 10:37:07 -0400 Subject: [PATCH 03/20] update to readme --- .../README.md | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 30df4ceb..b20cbbf4 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -1,42 +1,3 @@ ---- -title: ESS ServiceNow ITSM โ€“ Get Request Details -type: extension -category: Copilot Studio / Employee Self-Service -summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees look up the status and details of a ServiceNow service catalog request (REQ number) via natural language. -author: - - Dean Cron -version: 1.0.0 -published: "2026-07-30" -tags: - - ESS - - Employee Self-Service - - ServiceNow - - ITSM - - service catalog - - sc_request -format: extension -whatItIs: >- - A two-topic + standalone flow extension for the Microsoft Employee Self-Service - (ESS) Copilot Studio agent. Employees can ask the agent about any service - catalog request (REQ prefix) and receive a rich adaptive card showing the - request number, state, description, who it's assigned to, and a direct link - to ServiceNow. -whyUseIt: - - The ESS shared ITSM orchestrator flow only supports incident (INC) lookups. - This extension adds sc_request (REQ) support without modifying any shared flows. - - Follows the ESS two-topic pattern (user-facing + system routing) for - consistency with out-of-the-box ESS topics. - - Uses the same msdyn_ServiceNowOutputFieldMapperPlugin for field renaming, - keeping the response shape compatible with standard ESS adaptive card patterns. -howToUse: |- - See the Setup section below. -prerequisites: - - Microsoft Employee Self-Service agent (deployed) - - ServiceNow ITSM extension pack installed in Copilot Studio - - An active ServiceNow connection in Power Platform (Entra ID auth recommended) - - ESS Maker Kit (for automated deployment via push.py) ---- - # ESS ServiceNow ITSM โ€“ Get Request Details ## ๐Ÿ“Œ Overview From 8213cec3a7ab421347f4795052fc8246a43dd042 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Thu, 30 Jul 2026 10:49:48 -0400 Subject: [PATCH 04/20] fix(ess): hardcode ESS agent schema name in topic dialog reference --- .../ESS/servicenow-itsm-get-request-details/README.md | 3 +-- .../topics/servicenow-itsm-get-request-details.mcs.yml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 30df4ceb..9e0a93a3 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -68,8 +68,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look 1. Open your ESS Maker Kit workspace and run `/setup` to connect to the target environment. 2. Copy the `topics/` files into `workspace/agents/{your-agent-slug}/topics/`. 3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{NEW-GUID}/`. -4. In **both topic files**, replace `{AGENT_SCHEMA_NAME}` with your agent's schema name (e.g. `msdyn_copilotforemployeeselfservicehr`). -5. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. +4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. 6. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). 7. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. 8. Run `python scripts/push.py` to deploy. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml index a6590af5..64475c6a 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml @@ -76,7 +76,7 @@ beginDialog: binding: SysId: =Topic.RequestIdentifier - dialog: {AGENT_SCHEMA_NAME}.topic.ess-hr-servicenow-itsm-system-get-request-details + dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-details output: binding: ServiceNowData: Topic.ServiceNowData From fc2c79335b8c1cb13b463d24cfc6763850d0be9e Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Thu, 30 Jul 2026 10:53:20 -0400 Subject: [PATCH 05/20] fix(ess): remove stale AGENT_SCHEMA_NAME placeholder from README --- .../ESS/servicenow-itsm-get-request-details/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 28b0dad2..8532936f 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -42,7 +42,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look 1. In Power Automate, import `workflow/workflow.json` as a new cloud flow. 2. Update the ServiceNow and Dataverse connection references to point to your environment's connections. 3. Turn the flow on. Note the flow's ID (visible in the Power Automate URL). -4. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` as the content โ€” replacing `{AGENT_SCHEMA_NAME}` with your schema name and `{FLOW_GUID}` with the flow ID from step 3. +4. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` as the content โ€” replacing `{FLOW_GUID}` with the flow ID from step 3. 5. Publish. --- From 21dcdebe6e0f7242d4a18d0c223de78b7ce09cdd Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Thu, 30 Jul 2026 11:45:34 -0400 Subject: [PATCH 06/20] feat(ess): add ServiceNow ITSM Get User Requests sample Adds a new ESS extension sample for listing a user's ServiceNow service catalog requests (REQ numbers). Components: - User-facing topic with RequestState extraction (active/inactive/all) and 15-min cache - System routing topic with InvokeFlowAction wiring - Standalone Power Automate flow querying sc_request filtered by user email + active state - README with setup instructions Pairs with the servicenow-itsm-get-request-details sample for full REQ management. --- .../README.md | 96 +++++++ ...-servicenow-itsm-get-user-requests.mcs.yml | 102 +++++++ ...enow-itsm-system-get-user-requests.mcs.yml | 66 +++++ .../workflow/metadata.yml | 13 + .../workflow/workflow.json | 270 ++++++++++++++++++ 5 files changed, 547 insertions(+) create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md new file mode 100644 index 00000000..7070a624 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -0,0 +1,96 @@ +--- +title: ESS ServiceNow ITSM โ€“ Get User Requests +type: extension +category: Copilot Studio / Employee Self-Service +summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees list their ServiceNow service catalog requests (REQ numbers) via natural language. +author: + - Dean Cron +version: 1.0.0 +published: "2026-07-30" +tags: + - ESS + - Employee Self-Service + - ServiceNow + - ITSM + - service catalog + - sc_request +format: extension +whatItIs: >- + A two-topic + standalone flow extension for the Microsoft Employee Self-Service + (ESS) Copilot Studio agent. Employees can ask the agent for a list of their + service catalog requests โ€” filtered by state (active, inactive, or all) โ€” and + receive a numbered list with direct ServiceNow links. +whyUseIt: + - The ESS shared ITSM orchestrator flow only supports listing incidents (INC). + This extension adds sc_request (REQ) list support without modifying any shared flows. + - Includes a 15-minute cache for "all requests" queries (matching the Get User Tickets pattern). + - Pairs naturally with the ESS ServiceNow ITSM โ€“ Get Request Details sample for + full REQ request management. +howToUse: |- + See the Setup section below. +prerequisites: + - Microsoft Employee Self-Service agent (deployed) + - ServiceNow ITSM extension pack installed in Copilot Studio + - An active ServiceNow connection in Power Platform (maker's embedded connection) + - ESS Maker Kit (for automated deployment via push.py) +--- + +# ESS ServiceNow ITSM โ€“ Get User Requests + +## ๐Ÿ“Œ Overview + +Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list a user's **service catalog requests** (REQ numbers) from ServiceNow. The built-in ESS ITSM orchestrator only handles incident lists (INC). This sample adds a standalone flow that queries the `sc_request` table directly. + +**Example conversations:** + +> User: "Show me my open requests" +> Agent: *[Numbered list of active REQ items with links]* + +> User: "What requests have I submitted?" +> Agent: *[Numbered list of all REQ items, most recently updated first]* + +--- + +## ๐Ÿ“ Files + +| File | Purpose | +|------|---------| +| `topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml` | User-facing topic โ€” trigger phrases, state extraction, cache check, list rendering | +| `topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml` | System topic โ€” routes to the standalone flow and populates the 15-min cache | +| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_request` filtered by user email + active state | +| `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | + +--- + +## ๐Ÿš€ Setup + +### Option A โ€” ESS Maker Kit (recommended) + +1. Open your ESS Maker Kit workspace and run `/setup` to connect to the target environment. +2. Copy the `topics/` files into `workspace/agents/{your-agent-slug}/topics/`. +3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-user-requests-{NEW-GUID}/`. +4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. +5. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). +6. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. +7. Run `python scripts/push.py` to deploy. +8. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector. +9. Run `python scripts/push.py --repair "Get User Requests"` to wire the topicโ†’flow link. +10. Publish the agent in Copilot Studio. + +### Option B โ€” Manual + +1. In Power Automate, create a new cloud flow. Add a **Copilot Studio โ†’ When Copilot Studio calls a flow** trigger with inputs: `UserIdentifier` (Text) and `IsActive` (Text). +2. Add a **ServiceNow โ†’ List Records** action on the `sc_request` table. Set the filter query to build from the inputs (active/inactive/all based on `IsActive`), select fields `number,short_description,request_state,requested_for,assigned_to,sys_updated_on,sys_id,price`, limit 10. +3. Add the **Dataverse โ†’ Perform an unbound action** step calling `msdyn_ServiceNowOutputFieldMapperPlugin` to rename the fields. +4. Return the mapped output via a **Respond to Copilot** action. +5. Note the flow ID and update the system topic's `flowId` field. +6. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` โ€” replacing `{FLOW_GUID}` with the flow ID from step 5. +7. Publish. + +--- + +## โš ๏ธ Known Limitations + +- **Activation via API fails** โ€” same as other ServiceNow standalone flows in this collection. Manual turn-on in Power Automate is required. +- **Embedded connection** โ€” the list flow uses `runtimeSource: embedded` (maker's connection), not the invoker's credentials. The filter `requested_for.email = UserIdentifier` ensures each user only sees their own requests, but ServiceNow row-level ACLs are evaluated against the maker's account. Ensure the maker's ServiceNow account has read access to `sc_request`. +- **Global cache variables** โ€” the topic uses `Global.ESS_ServiceNow_AllRequests` and `Global.ESS_ServiceNow_NextAllRequestsRefresh`. These are agent-scoped global variables โ€” ensure they don't conflict with other topics in your agent. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml new file mode 100644 index 00000000..4723b15b --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml @@ -0,0 +1,102 @@ +kind: AdaptiveDialog +inputs: + - kind: AutomaticTaskInput + propertyName: RequestState + description: |- + RequestState indicates the state of the requests the user wants to fetch. For active, open and pending requests, RequestState value will be 'active'. Only for closed/completed requests, RequestState value will be 'inactive'. If not sure of the state or if the user is requesting all requests, RequestState will be 'all'. + Examples: + query - "List of my open requests"; RequestState value - "active" + query - "My pending requests"; RequestState value - "active" + query - "List of my requests"; RequestState - "all" + query - "My completed requests"; RequestState value - "inactive" + query - "My closed requests"; RequestState value - "inactive" + entity: StringPrebuiltEntity + shouldPromptUser: false + +modelDescription: |- + This tool retrieves the list of service catalog requests (REQ numbers) for the user. + + Use this tool when a user asks about their service requests, catalog requests, or REQ items โ€” NOT for incident tickets (INC). Use ServiceNow-ITSM-Get-User-Tickets for incidents. + + Example queries handled by this tool include: + * "Show me my service requests" + * "What requests do I have open?" + * "My pending catalog requests" + * "List my completed requests" + * "What did I request recently?" + * "Show my REQ items" + + Use these instructions to respond: + 1. Use the 'ServiceNowData' variable to provide the user with relevant information about their requests. + 2. If no requests are found, inform the user of the same. + 3. If requests are found, inform the user that the requests are sorted by most recently updated. + 4. Display them in a numbered list in the format: + `**[{RequestNumber}]({ServiceNowPortalBaseURI}?id=ticket&table=sc_request&sys_id={SysId})**: {ShortDescription} ({State})`. + 5. Let the user know you can provide more details on any specific request. +beginDialog: + kind: OnRecognizedIntent + id: main + intent: {} + actions: + - kind: ConditionGroup + id: conditionGroup_checkCache + conditions: + - id: conditionItem_useCache + condition: =!IsBlank(Global.ESS_ServiceNow_AllRequests) && !IsBlank(Global.ESS_ServiceNow_NextAllRequestsRefresh) && Global.ESS_ServiceNow_NextAllRequestsRefresh > Now() && (Topic.RequestState = "all" || IsBlank(Topic.RequestState)) + displayName: Requests list is cached? + actions: + - kind: SetVariable + id: useCachedRequests + variable: Topic.ServiceNowData + value: =Global.ESS_ServiceNow_AllRequests + + - id: conditionItem_allRequests + condition: =Topic.RequestState = "all" || IsBlank(Topic.RequestState) + displayName: List all requests + actions: + - kind: BeginDialog + id: callSystemGetAllRequests + input: + binding: + IsActive: =Blank() + UserIdentifier: =Global.ESS_UserContext_UPN + + dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-user-requests + output: + binding: + ServiceNowData: Topic.ServiceNowData + + elseActions: + - kind: SetVariable + id: setIsActive + variable: Topic.IsActive + value: =If(Topic.RequestState = "active", true, false) + + - kind: BeginDialog + id: callSystemGetFilteredRequests + input: + binding: + IsActive: =Topic.IsActive + UserIdentifier: =Global.ESS_UserContext_UPN + + dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-user-requests + output: + binding: + ServiceNowData: Topic.ServiceNowData + +inputType: + properties: + RequestState: + displayName: RequestState + description: |- + RequestState indicates the state of the requests the user wants to fetch. For active, open and pending requests, RequestState value will be 'active'. Only for closed/completed requests, RequestState value will be 'inactive'. If not sure of the state or if the user is requesting all requests, RequestState will be 'all'. + type: String + examples: + +outputType: + properties: + ServiceNowData: + displayName: ServiceNowData + description: List of service catalog requests returned from ServiceNow. + type: String + examples: diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml new file mode 100644 index 00000000..71aca5f4 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml @@ -0,0 +1,66 @@ +kind: AdaptiveDialog +inputs: + - kind: ManualTaskInput + propertyName: IsActive + value: =true + + - kind: ManualTaskInput + propertyName: UserIdentifier + value: "=\"\"" + +beginDialog: + kind: OnRedirect + id: main + actions: + - kind: InvokeFlowAction + id: callGetUserRequestsFlow + input: + binding: + text: =Topic.UserIdentifier + text_1: =If(IsBlank(Topic.IsActive), "", Text(Topic.IsActive)) + + output: + binding: + errormessage: Topic.ErrorMessage + issuccessful: Topic.IsSuccessful + servicenowdata: Topic.ServiceNowData + servicenowsessionid: Topic.ServiceNowSessionID + servicenowtransactionid: Topic.ServiceNowTransactionID + statuscode: Topic.StatusCode + + flowId: {FLOW_GUID} + + - kind: ConditionGroup + id: conditionGroup_cacheResult + conditions: + - id: conditionItem_cacheAll + condition: =!IsBlank(Topic.ServiceNowData) && IsBlank(Topic.IsActive) + displayName: Cache all-requests response? + actions: + - kind: SetVariable + id: cacheAllRequests + variable: Global.ESS_ServiceNow_AllRequests + value: =Topic.ServiceNowData + + - kind: SetVariable + id: setCacheExpiry + variable: Global.ESS_ServiceNow_NextAllRequestsRefresh + value: =DateAdd(Now(), 15, TimeUnit.Minutes) + +inputType: + properties: + IsActive: + displayName: IsActive + type: Boolean + + UserIdentifier: + displayName: UserIdentifier + type: String + +outputType: + properties: + ServiceNowData: + displayName: ServiceNowData + description: List of service catalog requests returned from ServiceNow. + type: String + diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml new file mode 100644 index 00000000..a39e18a0 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml @@ -0,0 +1,13 @@ +jsonFileName: workflows/workflow/workflow.json +workflowId: {FLOW_GUID} +name: ESS HR ServiceNow ITSM Get User Requests +type: 1 +description: "Retrieves a list of service catalog requests (sc_request) for the current user." +subprocess: false +category: 5 +mode: 0 +scope: 4 +stateCode: 1 +statusCode: 2 +isTransacted: true + diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json new file mode 100644 index 00000000..9202be96 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json @@ -0,0 +1,270 @@ +{ + "schemaVersion": "1.0.0.0", + "properties": { + "connectionReferences": { + "shared_service-now": { + "api": { + "name": "shared_service-now" + }, + "connection": { + "connectionReferenceLogicalName": "{SERVICENOW_CONNREF}" + }, + "runtimeSource": "embedded" + }, + "shared_commondataserviceforapps": { + "api": { + "name": "shared_commondataserviceforapps" + }, + "connection": { + "connectionReferenceLogicalName": "{DATAVERSE_CONNREF}" + }, + "runtimeSource": "embedded" + } + }, + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$authentication": { + "defaultValue": {}, + "type": "SecureObject" + }, + "$connections": { + "defaultValue": {}, + "type": "Object" + } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "text": { + "description": "The email or UPN of the current user.", + "title": "UserIdentifier", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + }, + "text_1": { + "description": "Filter by active state: 'true' for active, 'false' for inactive, empty for all.", + "title": "IsActive", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + } + }, + "required": ["text"] + } + } + } + }, + "actions": { + "Initialize_variable_-_ResponseBody": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ResponseBody", + "type": "object" + } + ] + }, + "runAfter": {} + }, + "Retrieve_Base_ITSM_Config": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "entityName": "msdyn_employeeselfservicetemplateconfigs", + "$select": "msdyn_value", + "$filter": "msdyn_uniquename eq 'msdyn_ServiceNowITSM'", + "$top": 1 + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "ListRecords", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": { + "Initialize_variable_-_ResponseBody": ["Succeeded"] + } + }, + "Build_Filter_Query": { + "type": "Compose", + "inputs": "@if(equals(triggerBody()?['text_1'], 'true'), concat('requested_for.email=', triggerBody()?['text'], '^active=true^ORDERBYDESCsys_updated_on'), if(equals(triggerBody()?['text_1'], 'false'), concat('requested_for.email=', triggerBody()?['text'], '^active=false^ORDERBYDESCsys_updated_on'), concat('requested_for.email=', triggerBody()?['text'], '^ORDERBYDESCsys_updated_on')))", + "runAfter": { + "Retrieve_Base_ITSM_Config": ["Succeeded"] + } + }, + "ServiceNow_Get_Requests": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "tableType": "sc_request", + "sysparm_query": "@outputs('Build_Filter_Query')", + "sysparm_display_value": true, + "sysparm_exclude_reference_link": false, + "sysparm_fields": "number,short_description,request_state,requested_for,assigned_to,sys_updated_on,sys_id,price", + "sysparm_limit": 10, + "sysparm_offset": 0 + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_service-now", + "operationId": "GetRecords", + "connectionName": "shared_service-now" + } + }, + "runAfter": { + "Build_Filter_Query": ["Succeeded"] + }, + "limit": { + "timeout": "PT1M" + } + }, + "Condition_-_ServiceNow_call_succeeded": { + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@outputs('ServiceNow_Get_Requests')?['statusCode']", + 200 + ] + }, + { + "less": [ + "@outputs('ServiceNow_Get_Requests')?['statusCode']", + 300 + ] + } + ] + }, + "actions": { + "Map_output_fields": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", + "item/ServiceNowTableData": "@body('ServiceNow_Get_Requests')?['value']", + "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"request_state\",\"OutputName\":\"State\"},{\"FieldName\":\"requested_for\",\"OutputName\":\"RequestedFor\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"},{\"FieldName\":\"price\",\"OutputName\":\"Price\"}]", + "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "PerformUnboundAction", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": {} + }, + "Set_success_response": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { + "ServiceNowData": "@outputs('Map_output_fields')?['body/ServiceNowMappedOutput']", + "ErrorMessage": "", + "StatusCode": "@outputs('ServiceNow_Get_Requests')?['statusCode']", + "IsSuccessful": "@true", + "ServiceNowSessionID": "@if(contains(outputs('ServiceNow_Get_Requests')?['headers/Set-Cookie'], 'glide_session_store'), first(split(last(split(outputs('ServiceNow_Get_Requests')?['headers/Set-Cookie'], 'glide_session_store=')), ';')), '')", + "ServiceNowTransactionID": "@outputs('ServiceNow_Get_Requests')?['headers/X-Transaction-ID']" + } + }, + "runAfter": { + "Map_output_fields": ["Succeeded"] + } + } + }, + "else": { + "actions": { + "Set_failure_response": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { + "ServiceNowData": "", + "ErrorMessage": "@{outputs('ServiceNow_Get_Requests')?['body/error/message']}", + "StatusCode": "@outputs('ServiceNow_Get_Requests')?['statusCode']", + "IsSuccessful": "@false", + "ServiceNowSessionID": "", + "ServiceNowTransactionID": "" + } + }, + "runAfter": {} + } + } + }, + "runAfter": { + "ServiceNow_Get_Requests": ["Succeeded", "Failed", "TimedOut", "Skipped"] + } + }, + "Respond_to_Copilot": { + "type": "Response", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "servicenowdata": { + "title": "ServiceNowData", + "x-ms-dynamically-added": true, + "type": "string", + "description": "List of requests returned from ServiceNow." + }, + "issuccessful": { + "title": "IsSuccessful", + "x-ms-dynamically-added": true, + "type": "boolean", + "description": "Boolean to indicate whether the flow succeeded or not." + }, + "errormessage": { + "title": "ErrorMessage", + "x-ms-dynamically-added": true, + "type": "string", + "description": "Error message in case of a failure." + }, + "statuscode": { + "title": "StatusCode", + "x-ms-dynamically-added": true, + "type": "number", + "description": "HTTP status code." + }, + "servicenowsessionid": { + "title": "ServiceNowSessionID", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + }, + "servicenowtransactionid": { + "title": "ServiceNowTransactionID", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + } + }, + "additionalProperties": {} + }, + "statusCode": 200, + "body": { + "servicenowdata": "@{variables('ResponseBody')?['ServiceNowData']}", + "issuccessful": "@variables('ResponseBody')?['IsSuccessful']", + "errormessage": "@{variables('ResponseBody')?['ErrorMessage']}", + "statuscode": "@variables('ResponseBody')?['StatusCode']", + "servicenowsessionid": "@{variables('ResponseBody')?['ServiceNowSessionID']}", + "servicenowtransactionid": "@{variables('ResponseBody')?['ServiceNowTransactionID']}" + } + }, + "runAfter": { + "Condition_-_ServiceNow_call_succeeded": ["Succeeded"] + } + } + } + } + } +} + From 3e17907e0b4e03f61a23d04d0b596c5fa8b9f01c Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 09:58:42 -0400 Subject: [PATCH 07/20] fix(ess): use GetRecords instead of GetRecord for REQ lookup by number --- .../README.md | 12 +- ...ervicenow-itsm-get-request-details.mcs.yml | 451 ++++++++++++------ .../workflow/metadata.yml | 2 +- .../workflow/workflow.json | 57 ++- 4 files changed, 357 insertions(+), 165 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 8532936f..213334b2 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -4,20 +4,25 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog requests** (REQ numbers) from ServiceNow. The built-in ESS ITSM orchestrator only handles incidents (INC). This sample adds a standalone flow that queries the `sc_request` table directly. +The user-facing topic also **routes RITM (request item) numbers** to the companion [`servicenow-itsm-get-request-item`](../servicenow-itsm-get-request-item/) sample โ€” install that sample too if you want RITM lookups to work. Without it, RITM input is still recognized but the routed dialog reference won't resolve. + **Example conversation:** > User: "What's the status of my request REQ0012345?" > Agent: *[Adaptive card showing request number, state, description, assigned to, last updated, and a link to open in ServiceNow]* +> User: "Look up RITM0001234" +> Agent: *[Adaptive card showing request item number, state, catalog item, parent request, assigned to, and a link to open in ServiceNow โ€” requires the companion `servicenow-itsm-get-request-item` sample]* + --- ## ๐Ÿ“ Files | File | Purpose | |------|---------| -| `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, adaptive card | +| `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, REQ/RITM routing, adaptive card | | `topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” routes to the standalone flow via `InvokeFlowAction` | -| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_request` by SysId | +| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_request` by REQ number | | `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | --- @@ -51,4 +56,5 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look - **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flow is created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 9 above). - **sc_request fields** โ€” the flow returns `request_state` (not `state`) and `requested_for` (not `caller_id`). The adaptive card handles this, but if you customise the output be aware the field names differ from the incident table. -- **SysId lookup only** โ€” the flow looks up by SysId. The user-facing topic extracts the REQ number or SysId from the user's message. If only a REQ number is provided (not a SysId), ensure your ServiceNow instance returns records when queried by `number` field โ€” you may need to adapt the flow to use a `GetRecords` filter instead of `GetRecord`. +- **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. +- **RITM lookups require the companion sample** โ€” the user-facing topic recognizes RITM numbers and routes them to `ess-hr-servicenow-itsm-system-get-request-item`, but that system topic and its flow live in the separate `servicenow-itsm-get-request-item` sample. Install both samples together for full REQ + RITM coverage. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml index 64475c6a..1354f9dc 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml @@ -7,19 +7,21 @@ inputs: - kind: AutomaticTaskInput propertyName: RequestIdentifier description: |- - `RequestIdentifier` is a unique value used to identify a service catalog request. + `RequestIdentifier` is a unique value used to identify a service catalog request or request item. - There are only two valid types of RequestIdentifiers: + There are three valid types of RequestIdentifiers: 1. **SysID** - A globally unique 32-character identifier. - Format: hexadecimal string (digits 0-9 and lowercase letters a-f). - Regex: `^[a-f0-9]{32}$` - 2. **RequestNumber** - - A unique identifier between 8-14 characters. + 2. **RequestNumber (REQ)** - Format: Always starts with the prefix `REQ` followed by digits. - Regex: `^REQ\d{5,}$` + 3. **RequestItemNumber (RITM)** + - Format: Always starts with the prefix `RITM` followed by digits. + - Regex: `^RITM\d{5,}$` - If neither identifier is available, ask the user to provide their REQ number. + If neither identifier is available, ask the user to provide their REQ or RITM number. **Important:** The extracted `SysID` or `RequestNumber` must be an exact match with the user query. Partial or fuzzy matches are not valid. entity: StringPrebuiltEntity @@ -32,7 +34,7 @@ inputs: Set `RelevantRequestFound = true` **only if** one of the following conditions is met: 1. **Exact Identifier Match** - - The user provides a `RequestNumber` (8-14 characters, starts with `"REQ"`) or a `SysID` (32-character hexadecimal string: digits `0-9` and lowercase letters `a-f`). + - The user provides a `RequestNumber` (starts with `"REQ"`), a `RequestItemNumber` (starts with `"RITM"`), or a `SysID` (32-character hexadecimal string). 2. **High-Relevance Descriptive Match** - The user describes a specific request with VERY HIGH SEMANTIC RELEVANCE. @@ -43,20 +45,19 @@ inputs: shouldPromptUser: false modelDescription: |- - Use this tool when a user asks for details about a specific service catalog request. Requests always start with the prefix REQ (e.g. REQ0012345). + Use this tool when a user asks for details about a specific service catalog request (REQ) or request item (RITM). - This tool retrieves details for a specific service catalog request identified by its REQ number or SysId. + This tool retrieves details for a REQ (service catalog request) or RITM (request item) identified by its number or SysId. Do NOT use this tool for incident tickets (which start with INC). Use ServiceNow-ITSM-Get-Ticket-Details for incidents instead. Example queries handled by this tool: * "What's the status of my request REQ0012345?" * "Show me details of REQ0067890" + * "Look up RITM0001234" + * "What's happening with RITM0005678?" * "Where is my service request?" - * "Can you look up request REQ0011223 for me?" - * "Details on my catalog request" * "Has my software request been approved?" - * "What's happening with my hardware request?" * "Check the status of my access request" beginDialog: kind: OnRecognizedIntent @@ -70,148 +71,316 @@ beginDialog: condition: =Topic.RelevantRequestFound = true displayName: Is a relevant request found? actions: - - kind: BeginDialog - id: callSystemGetRequestDetails - input: - binding: - SysId: =Topic.RequestIdentifier - - dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-details - output: - binding: - ServiceNowData: Topic.ServiceNowData - - kind: ConditionGroup - id: conditionGroup_dataFound + id: conditionGroup_ritmOrReq conditions: - - id: conditionItem_hasData - condition: =!IsBlank(Topic.ServiceNowData) - displayName: Was request data returned? + - id: conditionItem_isRitm + condition: =StartsWith(Upper(Topic.RequestIdentifier), "RITM") + displayName: Is this a RITM (request item)? actions: - - kind: ParseValue - id: parseRequestDetails - variable: Topic.RequestDetails - valueType: - kind: Record - properties: - AssignedTo: - type: String - examples: - - Comments: - type: String - examples: - - Description: - type: String - examples: - - LastUpdated: - type: String - examples: - - RequestedFor: - type: String - examples: - - RequestNumber: - type: String - examples: - - ServiceNowPortalBaseURI: - type: String - examples: - - ShortDescription: - type: String - examples: - - State: - type: String - examples: - - SysId: - type: String - examples: - - value: =Topic.ServiceNowData - - - kind: SetVariable - id: setFirstComment - variable: Topic.FirstComment - value: =First(Split(Topic.RequestDetails.Comments, Char(10) & Char(10))).Value - - - kind: SetTextVariable - id: setRequestTitle - variable: Topic.RequestTitle - value: "Service Request #{Topic.RequestDetails.RequestNumber} | " - - - kind: SendActivity - id: sendRequestCard - activity: - text: - - You can view the request details below. - attachments: - - kind: AdaptiveCardTemplate - cardContent: |- - ={ - type: "AdaptiveCard", - '$schema': "http://adaptivecards.io/schemas/adaptive-card.json", - version: "1.3", - body: [ - { - type: "Container", - items: [ - { - type: "RichTextBlock", - inlines: [ - {type: "TextRun", text: Topic.RequestTitle, weight: "Bolder"}, - {type: "TextRun", text: Topic.RequestDetails.State, color: If(Lower(Topic.RequestDetails.State) = "approved", "good", "default"), weight: "Bolder"} + # Requires the companion "servicenow-itsm-get-request-item" sample to be installed + # (provides the ess-hr-servicenow-itsm-system-get-request-item system topic + flow). + - kind: BeginDialog + id: callSystemGetRequestItem + input: + binding: + SysId: =Topic.RequestIdentifier + + dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-item + output: + binding: + ServiceNowData: Topic.ServiceNowData + + - kind: ConditionGroup + id: conditionGroup_ritmDataFound + conditions: + - id: conditionItem_ritmHasData + condition: =!IsBlank(Topic.ServiceNowData) + displayName: Was request item data returned? + actions: + - kind: ParseValue + id: parseRitmDetails + variable: Topic.RitmDetails + valueType: + kind: Record + properties: + AssignedTo: + type: String + examples: + CatalogItem: + type: String + examples: + Description: + type: String + examples: + LastUpdated: + type: String + examples: + ParentRequest: + type: String + examples: + Quantity: + type: String + examples: + RequestItemNumber: + type: String + examples: + ServiceNowPortalBaseURI: + type: String + examples: + ShortDescription: + type: String + examples: + State: + type: String + examples: + SysId: + type: String + examples: + + value: =Topic.ServiceNowData + + - kind: SendActivity + id: sendRitmCard + activity: + text: + - You can view the request item details below. + attachments: + - kind: AdaptiveCardTemplate + cardContent: |- + ={ + type: "AdaptiveCard", + '$schema': "http://adaptivecards.io/schemas/adaptive-card.json", + version: "1.3", + body: [ + { + type: "Container", + items: [ + { + type: "RichTextBlock", + inlines: [ + {type: "TextRun", text: "Request Item " & Topic.RitmDetails.RequestItemNumber & " | ", weight: "Bolder"}, + {type: "TextRun", text: Topic.RitmDetails.State, weight: "Bolder", + color: If(Lower(Topic.RitmDetails.State) = "approved", "good", "default")} + ] + }, + {type: "TextBlock", text: Topic.RitmDetails.ShortDescription, wrap: true, weight: "Bolder", size: "Medium"}, + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "50", items: [ + {type: "TextBlock", text: "Catalog item", isSubtle: true, wrap: true}, + {type: "TextBlock", text: If(IsBlank(Topic.RitmDetails.CatalogItem), "โ€”", Topic.RitmDetails.CatalogItem), wrap: true, spacing: "None"} + ]}, + {type: "Column", width: "50", items: [ + {type: "TextBlock", text: "Parent request", isSubtle: true, wrap: true}, + {type: "TextBlock", text: If(IsBlank(Topic.RitmDetails.ParentRequest), "โ€”", Topic.RitmDetails.ParentRequest), wrap: true, spacing: "None"} + ]} + ] + }, + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "50", items: [ + {type: "TextBlock", text: "Assigned to", isSubtle: true, wrap: true}, + {type: "TextBlock", text: If(IsBlank(Topic.RitmDetails.AssignedTo), "No agent assigned", Topic.RitmDetails.AssignedTo), wrap: true, spacing: "None"} + ]}, + {type: "Column", width: "50", items: [ + {type: "TextBlock", text: "Quantity", isSubtle: true, wrap: true}, + {type: "TextBlock", text: If(IsBlank(Topic.RitmDetails.Quantity), "1", Topic.RitmDetails.Quantity), wrap: true, spacing: "None"} + ]} + ] + }, + {type: "TextBlock", text: "Last updated", isSubtle: true, wrap: true}, + {type: "TextBlock", text: Text(DateValue(Topic.RitmDetails.LastUpdated), "mmmm d, yyyy"), wrap: true, spacing: "None"} + ] + }, + { + type: "Container", + separator: true, + spacing: "ExtraLarge", + items: [{ + type: "ColumnSet", + columns: [ + {type: "Column", width: "auto", items: [{ + type: "TextBlock", + text: "[Open in ServiceNow](" & Topic.RitmDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_req_item&sys_id=" & Topic.RitmDetails.SysId & ")", + wrap: true, weight: "Bolder" + }]}, + {type: "Column", width: "auto", + selectAction: {type: "Action.OpenUrl", url: Topic.RitmDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_req_item&sys_id=" & Topic.RitmDetails.SysId}, + items: [{type: "Image", url: Topic.IconBase64}]} + ] + }] + } ] - }, - {type: "TextBlock", text: Topic.RequestDetails.ShortDescription, wrap: true, weight: "Bolder", size: "Medium"}, + } + + - kind: CancelAllDialogs + id: cancelAfterRitm + + elseActions: + - kind: SendActivity + id: sendRitmNotFound + activity: + text: + - I couldn't find a request item matching that RITM number. Please check the number and try again. + + - kind: CancelAllDialogs + id: cancelAfterRitmNotFound + + elseActions: + - kind: BeginDialog + id: callSystemGetRequestDetails + input: + binding: + SysId: =Topic.RequestIdentifier + + dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-details + output: + binding: + ServiceNowData: Topic.ServiceNowData + + + - kind: ConditionGroup + id: conditionGroup_dataFound + conditions: + - id: conditionItem_hasData + condition: =!IsBlank(Topic.ServiceNowData) && !StartsWith(Upper(Topic.RequestIdentifier), "RITM") + displayName: Was request data returned? + actions: + - kind: ParseValue + id: parseRequestDetails + variable: Topic.RequestDetails + valueType: + kind: Record + properties: + AssignedTo: + type: String + examples: + + Comments: + type: String + examples: + + Description: + type: String + examples: + + LastUpdated: + type: String + examples: + + RequestedFor: + type: String + examples: + + RequestNumber: + type: String + examples: + + ServiceNowPortalBaseURI: + type: String + examples: + + ShortDescription: + type: String + examples: + + State: + type: String + examples: + + SysId: + type: String + examples: + + value: =Topic.ServiceNowData + + - kind: SetVariable + id: setFirstComment + variable: Topic.FirstComment + value: =First(Split(Topic.RequestDetails.Comments, Char(10) & Char(10))).Value + + - kind: SetTextVariable + id: setRequestTitle + variable: Topic.RequestTitle + value: "Service Request #{Topic.RequestDetails.RequestNumber} | " + + - kind: SendActivity + id: sendRequestCard + activity: + text: + - You can view the request details below. + attachments: + - kind: AdaptiveCardTemplate + cardContent: |- + ={ + type: "AdaptiveCard", + '$schema': "http://adaptivecards.io/schemas/adaptive-card.json", + version: "1.3", + body: [ { - type: "ColumnSet", - columns: [ - {type: "Column", width: "50", items: [{type: "TextBlock", text: "Requested for", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.RequestedFor), "โ€”", Topic.RequestDetails.RequestedFor), wrap: true, spacing: "None"}]}, - {type: "Column", width: "50", items: [{type: "TextBlock", text: "Assigned to", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.AssignedTo), "No agent assigned", Topic.RequestDetails.AssignedTo), wrap: true, spacing: "None"}]} + type: "Container", + items: [ + { + type: "RichTextBlock", + inlines: [ + {type: "TextRun", text: Topic.RequestTitle, weight: "Bolder"}, + {type: "TextRun", text: Topic.RequestDetails.State, color: If(Lower(Topic.RequestDetails.State) = "approved", "good", "default"), weight: "Bolder"} + ] + }, + {type: "TextBlock", text: Topic.RequestDetails.ShortDescription, wrap: true, weight: "Bolder", size: "Medium"}, + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "50", items: [{type: "TextBlock", text: "Requested for", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.RequestedFor), "โ€”", Topic.RequestDetails.RequestedFor), wrap: true, spacing: "None"}]}, + {type: "Column", width: "50", items: [{type: "TextBlock", text: "Assigned to", isSubtle: true, wrap: true}, {type: "TextBlock", text: If(IsBlank(Topic.RequestDetails.AssignedTo), "No agent assigned", Topic.RequestDetails.AssignedTo), wrap: true, spacing: "None"}]} + ] + }, + {type: "ColumnSet", columns: [{type: "Column", width: "100", items: [{type: "TextBlock", text: "Last updated", wrap: true, isSubtle: true}, {type: "TextBlock", text: Text(DateValue(Topic.RequestDetails.LastUpdated), "mmmm d, yyyy"), wrap: true, spacing: "None"}]}]}, + If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: "Description", wrap: true, isSubtle: true}), + If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: Topic.RequestDetails.Description, wrap: true, spacing: "None"}), + If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: "Latest comment", wrap: true, isSubtle: true}), + If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: Mid(Topic.FirstComment, Find(Char(10), Topic.FirstComment) + 1, Len(Topic.FirstComment)), wrap: true, spacing: "None"}), + {type: "TextBlock", text: "SysId", isVisible: false}, + {type: "TextBlock", text: Topic.RequestDetails.SysId, isVisible: false} ] }, - {type: "ColumnSet", columns: [{type: "Column", width: "100", items: [{type: "TextBlock", text: "Last updated", wrap: true, isSubtle: true}, {type: "TextBlock", text: Text(DateValue(Topic.RequestDetails.LastUpdated), "mmmm d, yyyy"), wrap: true, spacing: "None"}]}]}, - If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: "Description", wrap: true, isSubtle: true}), - If(IsBlank(Topic.RequestDetails.Description),{},{type: "TextBlock", text: Topic.RequestDetails.Description, wrap: true, spacing: "None"}), - If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: "Latest comment", wrap: true, isSubtle: true}), - If(IsBlank(Topic.RequestDetails.Comments),{},{type: "TextBlock", text: Mid(Topic.FirstComment, Find(Char(10), Topic.FirstComment) + 1, Len(Topic.FirstComment)), wrap: true, spacing: "None"}), - {type: "TextBlock", text: "SysId", isVisible: false}, - {type: "TextBlock", text: Topic.RequestDetails.SysId, isVisible: false} - ] - }, - { - type: "Container", - separator: true, - spacing: "ExtraLarge", - items: [ { - type: "ColumnSet", - columns: [ - {type: "Column", width: "auto", items: [{type: "TextBlock", text: "[Open in ServiceNow](" & Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId & ")", wrap: true, weight: "Bolder"}]}, - {type: "Column", width: "auto", selectAction: {type: "Action.OpenUrl", url: Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId}, items: [{type: "Image", url: Topic.IconBase64}]} + type: "Container", + separator: true, + spacing: "ExtraLarge", + items: [ + { + type: "ColumnSet", + columns: [ + {type: "Column", width: "auto", items: [{type: "TextBlock", text: "[Open in ServiceNow](" & Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId & ")", wrap: true, weight: "Bolder"}]}, + {type: "Column", width: "auto", selectAction: {type: "Action.OpenUrl", url: Topic.RequestDetails.ServiceNowPortalBaseURI & "?id=ticket&table=sc_request&sys_id=" & Topic.RequestDetails.SysId}, items: [{type: "Image", url: Topic.IconBase64}]} + ] + } ] } ] } - ] - } - - - kind: CancelAllDialogs - id: cancelAllDialogs - elseActions: - - kind: SendActivity - id: sendNotFound - activity: - text: - - I couldn't find a service request matching that number. Please check the REQ number and try again. + - kind: CancelAllDialogs + id: cancelAllDialogs + + elseActions: + - kind: ConditionGroup + id: conditionGroup_reqNotFoundGuard + conditions: + - id: conditionItem_notRitm + condition: =!StartsWith(Upper(Topic.RequestIdentifier), "RITM") + displayName: Not a RITM โ€” show REQ not found + actions: + - kind: SendActivity + id: sendNotFound + activity: + text: + - I couldn't find a service request matching that number. Please check the REQ number and try again. + elseActions: [] elseActions: - kind: SetVariable @@ -233,7 +402,7 @@ inputType: RequestIdentifier: displayName: RequestIdentifier - description: REQ number or SysId of the service catalog request. + description: REQ or RITM number, or SysId, of the service catalog request/item. type: String examples: diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml index 5649d6fc..c277df5f 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml @@ -6,7 +6,7 @@ jsonFileName: workflow/workflow.json workflowId: "{FLOW_GUID}" name: ESS HR ServiceNow ITSM Get Request Details type: 1 -description: "Retrieves details for a specific ServiceNow service catalog request (sc_request table) by SysId." +description: "Retrieves details for a specific ServiceNow service catalog request (sc_request table) by REQ number." subprocess: false category: 5 mode: 0 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json index 2c3bfb8c..f7f65985 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json @@ -43,7 +43,7 @@ "type": "object", "properties": { "text": { - "description": "System Identifier (SysId) of the service catalog request to retrieve.", + "description": "REQ number (e.g. REQ0012345) of the service catalog request to retrieve.", "title": "SysId", "type": "string", "x-ms-content-hint": "TEXT", @@ -92,14 +92,15 @@ "inputs": { "parameters": { "tableType": "sc_request", - "sysid": "@triggerBody()?['text']", + "sysparm_query": "@concat('number=', triggerBody()?['text'])", + "sysparm_limit": 1, "sysparm_display_value": true, "sysparm_exclude_reference_link": false, "sysparm_fields": "number,short_description,description,request_state,requested_for,assigned_to,comments,sys_updated_on,sys_id" }, "host": { "apiId": "/providers/Microsoft.PowerApps/apis/shared_service-now", - "operationId": "GetRecord", + "operationId": "GetRecords", "connectionName": "shared_service-now" } }, @@ -115,26 +116,40 @@ "expression": { "and": [ { - "greaterOrEquals": [ - "@outputs('ServiceNow_Get_Request')?['statusCode']", - 200 - ] - }, - { - "less": [ - "@outputs('ServiceNow_Get_Request')?['statusCode']", - 300 + "greater": [ + "@length(coalesce(body('ServiceNow_Get_Request')?['result'], json('[]')))", + 0 ] } ] }, "actions": { + "Store_First_Result": { + "type": "Compose", + "inputs": "@first(body('ServiceNow_Get_Request')?['result'])", + "runAfter": {} + }, + "Flatten_Reference_Fields": { + "type": "Compose", + "inputs": { + "number": "@outputs('Store_First_Result')?['number']", + "short_description": "@outputs('Store_First_Result')?['short_description']", + "description": "@outputs('Store_First_Result')?['description']", + "request_state": "@outputs('Store_First_Result')?['request_state']", + "requested_for": "@{if(startsWith(string(outputs('Store_First_Result')?['requested_for']), '{'), json(string(outputs('Store_First_Result')?['requested_for']))?['display_value'], string(outputs('Store_First_Result')?['requested_for']))}", + "assigned_to": "@{if(startsWith(string(outputs('Store_First_Result')?['assigned_to']), '{'), json(string(outputs('Store_First_Result')?['assigned_to']))?['display_value'], string(outputs('Store_First_Result')?['assigned_to']))}", + "comments": "@outputs('Store_First_Result')?['comments']", + "sys_updated_on": "@outputs('Store_First_Result')?['sys_updated_on']", + "sys_id": "@outputs('Store_First_Result')?['sys_id']" + }, + "runAfter": { "Store_First_Result": ["Succeeded"] } + }, "Map_output_fields": { "type": "OpenApiConnection", "inputs": { "parameters": { "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", - "item/ServiceNowTableData": "@body('ServiceNow_Get_Request')?['result']", + "item/ServiceNowTableData": "@outputs('Flatten_Reference_Fields')", "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"description\",\"OutputName\":\"Description\"},{\"FieldName\":\"request_state\",\"OutputName\":\"State\"},{\"FieldName\":\"requested_for\",\"OutputName\":\"RequestedFor\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"comments\",\"OutputName\":\"Comments\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"}]", "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" }, @@ -144,7 +159,9 @@ "connectionName": "shared_commondataserviceforapps" } }, - "runAfter": {} + "runAfter": { + "Flatten_Reference_Fields": ["Succeeded"] + } }, "Set_success_response": { "type": "SetVariable", @@ -153,10 +170,10 @@ "value": { "ServiceNowData": "@outputs('Map_output_fields')?['body/ServiceNowMappedOutput']", "ErrorMessage": "", - "StatusCode": "@outputs('ServiceNow_Get_Request')?['statusCode']", + "StatusCode": 200, "IsSuccessful": "@true", - "ServiceNowSessionID": "@if(contains(outputs('ServiceNow_Get_Request')?['headers/Set-Cookie'], 'glide_session_store'), first(split(last(split(outputs('ServiceNow_Get_Request')?['headers/Set-Cookie'], 'glide_session_store=')), ';')), '')", - "ServiceNowTransactionID": "@outputs('ServiceNow_Get_Request')?['headers/X-Transaction-ID']" + "ServiceNowSessionID": "", + "ServiceNowTransactionID": "" } }, "runAfter": { @@ -172,8 +189,8 @@ "name": "ResponseBody", "value": { "ServiceNowData": "", - "ErrorMessage": "@{outputs('ServiceNow_Get_Request')?['body/error/message']}", - "StatusCode": "@outputs('ServiceNow_Get_Request')?['statusCode']", + "ErrorMessage": "No service catalog request found.", + "StatusCode": 404, "IsSuccessful": "@false", "ServiceNowSessionID": "", "ServiceNowTransactionID": "" @@ -184,7 +201,7 @@ } }, "runAfter": { - "ServiceNow_Get_Request": ["Succeeded", "Failed", "TimedOut", "Skipped"] + "ServiceNow_Get_Request": ["Succeeded", "Failed"] } }, "Respond_to_Copilot": { From 09bf764d5c5d6638b9ba0846cb59404ca6abaa07 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 09:58:49 -0400 Subject: [PATCH 08/20] feat(ess): add ServiceNow ITSM Get Request Item (RITM) sample --- .../README.md | 58 ++++++ ...cenow-itsm-system-get-request-item.mcs.yml | 38 ++++ .../workflow/metadata.yml | 16 ++ .../workflow/workflow.json | 183 ++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md new file mode 100644 index 00000000..0e1e98da --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md @@ -0,0 +1,58 @@ +# ESS ServiceNow ITSM โ€“ Get Request Item (RITM) + +## ๐Ÿ“Œ Overview + +Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog request items** (RITM numbers) from ServiceNow. This sample is the RITM counterpart to [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) โ€” it adds a standalone flow that queries the `sc_req_item` table directly. + +**This sample depends on `servicenow-itsm-get-request-details`.** The routing logic that decides "is this a REQ or a RITM?" lives in that sample's user-facing topic (`servicenow-itsm-get-request-details.mcs.yml`). Install both samples together โ€” this one only supplies the system topic + flow that the shared topic redirects to for RITM numbers. + +**Example conversation:** + +> User: "Look up RITM0001234" +> Agent: *[Adaptive card showing request item number, state, catalog item, parent request, assigned to, quantity, last updated, and a link to open in ServiceNow]* + +--- + +## ๐Ÿ“ Files + +| File | Purpose | +|------|---------| +| `topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` | System topic โ€” routes to the standalone flow via `InvokeFlowAction` | +| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_req_item` by RITM number | +| `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | + +> Note: there is no separate user-facing topic in this sample. RITM lookups are triggered by `servicenow-itsm-get-request-details.mcs.yml` (installed as part of the companion sample), which recognizes RITM-prefixed identifiers and redirects to the system topic here. + +--- + +## ๐Ÿš€ Setup + +### Option A โ€” ESS Maker Kit (recommended) + +1. Install and set up the companion [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) sample first (its user-facing topic is required for RITM routing to work). +2. Copy the `topics/` file into `workspace/agents/{your-agent-slug}/topics/`. +3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{NEW-GUID}/`. +4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. +5. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). +6. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. +7. Run `python scripts/push.py` to deploy. +8. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector with `runtimeSource: invoker`. +9. Run `python scripts/push.py --repair "Get Request Item"` to wire the topicโ†’flow link. +10. Publish the agent in Copilot Studio. + +### Option B โ€” Manual + +1. In Power Automate, import `workflow/workflow.json` as a new cloud flow. +2. Update the ServiceNow and Dataverse connection references to point to your environment's connections. +3. Turn the flow on. Note the flow's ID (visible in the Power Automate URL). +4. In Copilot Studio, open your ESS agent and create a new system topic using the YAML in `topics/` as the content โ€” replacing `{FLOW_GUID}` with the flow ID from step 3. +5. Make sure the `servicenow-itsm-get-request-details` topic (from the companion sample) is installed and its `BeginDialog` reference to `ess-hr-servicenow-itsm-system-get-request-item` resolves to this new system topic. +6. Publish. + +--- + +## โš ๏ธ Known Limitations + +- **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flow is created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 8 above). +- **Requires the companion sample** โ€” this sample has no standalone trigger phrases of its own. Without `servicenow-itsm-get-request-details` installed, RITM input will not be routed here. +- **Reference fields** โ€” `cat_item`, `request`, and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml new file mode 100644 index 00000000..323f9ddc --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml @@ -0,0 +1,38 @@ +kind: AdaptiveDialog +inputs: + - kind: ManualTaskInput + propertyName: SysId + value: ="" + +beginDialog: + kind: OnRedirect + id: main + actions: + - kind: InvokeFlowAction + id: callGetRequestItemFlow + input: + binding: + text: =Topic.SysId + output: + binding: + errormessage: Topic.ErrorMessage + issuccessful: Topic.IsSuccessful + servicenowdata: Topic.ServiceNowData + servicenowsessionid: Topic.ServiceNowSessionID + servicenowtransactionid: Topic.ServiceNowTransactionID + statuscode: Topic.StatusCode + # Replace {FLOW_GUID} with the workflowId from workflow/metadata.yml + flowId: "{FLOW_GUID}" + +inputType: + properties: + SysId: + displayName: SysId + type: String + +outputType: + properties: + ServiceNowData: + displayName: ServiceNowData + description: Request item details data returned from ServiceNow. + type: String diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml new file mode 100644 index 00000000..b25f5030 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml @@ -0,0 +1,16 @@ +# Replace {FLOW_GUID} with a newly generated GUID (e.g. from: python -c "import uuid; print(uuid.uuid4())") +# Replace {SERVICENOW_CONNREF} with your agent's ServiceNow connection reference logical name +# Replace {DATAVERSE_CONNREF} with your agent's Dataverse connection reference logical name +# Both connrefs can be found in workspace/agents/{slug}/connectionreferences.mcs.yml +jsonFileName: workflow/workflow.json +workflowId: "{FLOW_GUID}" +name: ESS HR ServiceNow ITSM Get Request Item +type: 1 +description: "Retrieves details for a ServiceNow request item (sc_req_item / RITM) by number." +subprocess: false +category: 5 +mode: 0 +scope: 4 +stateCode: 1 +statusCode: 2 +isTransacted: true diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json new file mode 100644 index 00000000..ccd55e98 --- /dev/null +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json @@ -0,0 +1,183 @@ +{ + "schemaVersion": "1.0.0.0", + "properties": { + "connectionReferences": { + "shared_service-now": { + "api": { "name": "shared_service-now" }, + "connection": { "connectionReferenceLogicalName": "{SERVICENOW_CONNREF}" }, + "runtimeSource": "invoker" + }, + "shared_commondataserviceforapps": { + "api": { "name": "shared_commondataserviceforapps" }, + "connection": { "connectionReferenceLogicalName": "{DATAVERSE_CONNREF}" }, + "runtimeSource": "embedded" + } + }, + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$authentication": { "defaultValue": {}, "type": "SecureObject" }, + "$connections": { "defaultValue": {}, "type": "Object" } + }, + "triggers": { + "manual": { + "type": "Request", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "text": { + "description": "RITM number (e.g. RITM0010002) of the request item to retrieve.", + "title": "Identifier", + "type": "string", + "x-ms-content-hint": "TEXT", + "x-ms-dynamically-added": true + } + }, + "required": ["text"] + } + } + } + }, + "actions": { + "Initialize_ResponseBody": { + "type": "InitializeVariable", + "inputs": { "variables": [{ "name": "ResponseBody", "type": "object" }] }, + "runAfter": {} + }, + "Retrieve_Base_ITSM_Config": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "entityName": "msdyn_employeeselfservicetemplateconfigs", + "$select": "msdyn_value", + "$filter": "msdyn_uniquename eq 'msdyn_ServiceNowITSM'", + "$top": 1 + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "ListRecords", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": { "Initialize_ResponseBody": ["Succeeded"] } + }, + "Find_Request_Item": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "tableType": "sc_req_item", + "sysparm_query": "@concat('number=', triggerBody()?['text'])", + "sysparm_fields": "number,short_description,state,cat_item,request,assigned_to,sys_updated_on,sys_id,quantity,description", + "sysparm_limit": 1, + "sysparm_display_value": true, + "sysparm_exclude_reference_link": false + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_service-now", + "operationId": "GetRecords", + "connectionName": "shared_service-now" + } + }, + "runAfter": { "Retrieve_Base_ITSM_Config": ["Succeeded"] }, + "limit": { "timeout": "PT1M" } + }, + "Condition_Record_Found": { + "type": "If", + "expression": { "and": [{ "greater": ["@length(coalesce(body('Find_Request_Item')?['result'], json('[]')))", 0] }] }, + "runAfter": { "Find_Request_Item": ["Succeeded", "Failed"] }, + "actions": { + "Store_First_Result": { + "type": "Compose", + "inputs": "@first(body('Find_Request_Item')?['result'])", + "runAfter": {} + }, + "Flatten_Reference_Fields": { + "type": "Compose", + "inputs": { + "number": "@outputs('Store_First_Result')?['number']", + "short_description": "@outputs('Store_First_Result')?['short_description']", + "description": "@outputs('Store_First_Result')?['description']", + "state": "@outputs('Store_First_Result')?['state']", + "cat_item": "@{if(startsWith(string(outputs('Store_First_Result')?['cat_item']), '{'), json(string(outputs('Store_First_Result')?['cat_item']))?['display_value'], string(outputs('Store_First_Result')?['cat_item']))}", + "request": "@{if(startsWith(string(outputs('Store_First_Result')?['request']), '{'), json(string(outputs('Store_First_Result')?['request']))?['display_value'], string(outputs('Store_First_Result')?['request']))}", + "assigned_to": "@{if(startsWith(string(outputs('Store_First_Result')?['assigned_to']), '{'), json(string(outputs('Store_First_Result')?['assigned_to']))?['display_value'], string(outputs('Store_First_Result')?['assigned_to']))}", + "quantity": "@outputs('Store_First_Result')?['quantity']", + "sys_updated_on": "@outputs('Store_First_Result')?['sys_updated_on']", + "sys_id": "@outputs('Store_First_Result')?['sys_id']" + }, + "runAfter": { "Store_First_Result": ["Succeeded"] } + }, + "Map_output_fields": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", + "item/ServiceNowTableData": "@outputs('Flatten_Reference_Fields')", + "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestItemNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"description\",\"OutputName\":\"Description\"},{\"FieldName\":\"state\",\"OutputName\":\"State\"},{\"FieldName\":\"cat_item\",\"OutputName\":\"CatalogItem\"},{\"FieldName\":\"request\",\"OutputName\":\"ParentRequest\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"quantity\",\"OutputName\":\"Quantity\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"}]", + "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "PerformUnboundAction", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": { "Flatten_Reference_Fields": ["Succeeded"] } + }, + "Set_success_response": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { "ServiceNowData": "@outputs('Map_output_fields')?['body/ServiceNowMappedOutput']", "ErrorMessage": "", "IsSuccessful": "@true", "ServiceNowSessionID": "", "ServiceNowTransactionID": "" } + }, + "runAfter": { "Map_output_fields": ["Succeeded"] } + } + }, + "else": { + "actions": { + "Set_not_found": { + "type": "SetVariable", + "inputs": { + "name": "ResponseBody", + "value": { "ServiceNowData": "", "ErrorMessage": "No request item found.", "IsSuccessful": "@false", "ServiceNowSessionID": "", "ServiceNowTransactionID": "" } + }, + "runAfter": {} + } + } + } + }, + "Respond_to_Copilot": { + "type": "Response", + "kind": "Skills", + "inputs": { + "schema": { + "type": "object", + "properties": { + "servicenowdata": { "title": "ServiceNowData", "x-ms-dynamically-added": true, "type": "string" }, + "issuccessful": { "title": "IsSuccessful", "x-ms-dynamically-added": true, "type": "boolean" }, + "errormessage": { "title": "ErrorMessage", "x-ms-dynamically-added": true, "type": "string" }, + "statuscode": { "title": "StatusCode", "x-ms-dynamically-added": true, "type": "number" }, + "servicenowsessionid": { "title": "ServiceNowSessionID", "type": "string", "x-ms-content-hint": "TEXT", "x-ms-dynamically-added": true }, + "servicenowtransactionid": { "title": "ServiceNowTransactionID", "type": "string", "x-ms-content-hint": "TEXT", "x-ms-dynamically-added": true } + }, + "additionalProperties": {} + }, + "statusCode": 200, + "body": { + "servicenowdata": "@{variables('ResponseBody')?['ServiceNowData']}", + "issuccessful": "@variables('ResponseBody')?['IsSuccessful']", + "errormessage": "@{variables('ResponseBody')?['ErrorMessage']}", + "statuscode": 200, + "servicenowsessionid": "", + "servicenowtransactionid": "" + } + }, + "runAfter": { "Condition_Record_Found": ["Succeeded", "Failed"] } + } + } + } + } +} From faa5d57480356947324ec6484014858d046634af Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:17:35 -0400 Subject: [PATCH 09/20] fix(ess): read GetRecords rows from 'result' and flatten reference fields in user requests list --- .../README.md | 2 + .../workflow/workflow.json | 45 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 7070a624..26591633 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -94,3 +94,5 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list - **Activation via API fails** โ€” same as other ServiceNow standalone flows in this collection. Manual turn-on in Power Automate is required. - **Embedded connection** โ€” the list flow uses `runtimeSource: embedded` (maker's connection), not the invoker's credentials. The filter `requested_for.email = UserIdentifier` ensures each user only sees their own requests, but ServiceNow row-level ACLs are evaluated against the maker's account. Ensure the maker's ServiceNow account has read access to `sc_request`. - **Global cache variables** โ€” the topic uses `Global.ESS_ServiceNow_AllRequests` and `Global.ESS_ServiceNow_NextAllRequestsRefresh`. These are agent-scoped global variables โ€” ensure they don't conflict with other topics in your agent. +- **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Request_Records` **Select** step normalizes these to plain display-value strings across every row before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. +- **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise this flow. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json index 9202be96..dabbdd61 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json @@ -131,26 +131,37 @@ "expression": { "and": [ { - "greaterOrEquals": [ - "@outputs('ServiceNow_Get_Requests')?['statusCode']", - 200 - ] - }, - { - "less": [ - "@outputs('ServiceNow_Get_Requests')?['statusCode']", - 300 + "greater": [ + "@length(coalesce(body('ServiceNow_Get_Requests')?['result'], json('[]')))", + 0 ] } ] }, "actions": { + "Flatten_Request_Records": { + "type": "Select", + "inputs": { + "from": "@coalesce(body('ServiceNow_Get_Requests')?['result'], json('[]'))", + "select": { + "number": "@item()?['number']", + "short_description": "@item()?['short_description']", + "request_state": "@item()?['request_state']", + "requested_for": "@{if(startsWith(string(item()?['requested_for']), '{'), json(string(item()?['requested_for']))?['display_value'], string(item()?['requested_for']))}", + "assigned_to": "@{if(startsWith(string(item()?['assigned_to']), '{'), json(string(item()?['assigned_to']))?['display_value'], string(item()?['assigned_to']))}", + "sys_updated_on": "@item()?['sys_updated_on']", + "sys_id": "@item()?['sys_id']", + "price": "@item()?['price']" + } + }, + "runAfter": {} + }, "Map_output_fields": { "type": "OpenApiConnection", "inputs": { "parameters": { "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", - "item/ServiceNowTableData": "@body('ServiceNow_Get_Requests')?['value']", + "item/ServiceNowTableData": "@body('Flatten_Request_Records')", "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"request_state\",\"OutputName\":\"State\"},{\"FieldName\":\"requested_for\",\"OutputName\":\"RequestedFor\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"},{\"FieldName\":\"price\",\"OutputName\":\"Price\"}]", "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" }, @@ -160,7 +171,9 @@ "connectionName": "shared_commondataserviceforapps" } }, - "runAfter": {} + "runAfter": { + "Flatten_Request_Records": ["Succeeded"] + } }, "Set_success_response": { "type": "SetVariable", @@ -169,10 +182,10 @@ "value": { "ServiceNowData": "@outputs('Map_output_fields')?['body/ServiceNowMappedOutput']", "ErrorMessage": "", - "StatusCode": "@outputs('ServiceNow_Get_Requests')?['statusCode']", + "StatusCode": 200, "IsSuccessful": "@true", - "ServiceNowSessionID": "@if(contains(outputs('ServiceNow_Get_Requests')?['headers/Set-Cookie'], 'glide_session_store'), first(split(last(split(outputs('ServiceNow_Get_Requests')?['headers/Set-Cookie'], 'glide_session_store=')), ';')), '')", - "ServiceNowTransactionID": "@outputs('ServiceNow_Get_Requests')?['headers/X-Transaction-ID']" + "ServiceNowSessionID": "", + "ServiceNowTransactionID": "" } }, "runAfter": { @@ -188,8 +201,8 @@ "name": "ResponseBody", "value": { "ServiceNowData": "", - "ErrorMessage": "@{outputs('ServiceNow_Get_Requests')?['body/error/message']}", - "StatusCode": "@outputs('ServiceNow_Get_Requests')?['statusCode']", + "ErrorMessage": "No requests found.", + "StatusCode": 404, "IsSuccessful": "@false", "ServiceNowSessionID": "", "ServiceNowTransactionID": "" From 75eb610fabe652500cf89b776588cf6470072b2f Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:23:10 -0400 Subject: [PATCH 10/20] docs(ess): tighten setup steps and call out OBO connection requirement --- .../README.md | 57 +++++++++++----- .../README.md | 60 ++++++++++++----- .../README.md | 67 ++++++++++++++----- 3 files changed, 135 insertions(+), 49 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 213334b2..96f06a1f 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -29,26 +29,53 @@ The user-facing topic also **routes RITM (request item) numbers** to the compani ## ๐Ÿš€ Setup +> **โš ๏ธ OBO (on-behalf-of) is required for this flow.** The ServiceNow connection uses +> `"runtimeSource": "invoker"`, so the flow calls ServiceNow **as the signed-in employee**, +> not as the maker. This is what keeps ServiceNow's own row-level ACLs in force โ€” each user +> only sees requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. + ### Option A โ€” ESS Maker Kit (recommended) -1. Open your ESS Maker Kit workspace and run `/setup` to connect to the target environment. -2. Copy the `topics/` files into `workspace/agents/{your-agent-slug}/topics/`. -3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{NEW-GUID}/`. -4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. -6. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). -7. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. -8. Run `python scripts/push.py` to deploy. -9. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector with `runtimeSource: invoker`. -10. Run `python scripts/push.py --repair "Get Request Details"` to wire the topicโ†’flow link. -11. Publish the agent in Copilot Studio. +1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. +2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. +3. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{NEW-GUID}/`. +4. Replace the placeholders: + - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 3). + - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. +5. `python scripts/push.py` +6. **Configure OBO** โ€” see below. Do this *before* turning the flow on. +7. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for invoker-auth ServiceNow flows). +8. `python scripts/push.py --repair "Get Request Details"` to wire topic โ†’ flow. +9. Publish the agent in Copilot Studio. ### Option B โ€” Manual -1. In Power Automate, import `workflow/workflow.json` as a new cloud flow. -2. Update the ServiceNow and Dataverse connection references to point to your environment's connections. -3. Turn the flow on. Note the flow's ID (visible in the Power Automate URL). -4. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` as the content โ€” replacing `{FLOW_GUID}` with the flow ID from step 3. -5. Publish. +1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. +2. Point the ServiceNow and Dataverse connection references at your environment's connections. +3. **Configure OBO** โ€” see below. +4. Turn the flow on and copy its ID from the URL. +5. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. +6. Publish. + +### ๐Ÿ” Configure OBO (required) + +The ServiceNow connector must authenticate as the end user, not the maker: + +1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. +2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user** + (OAuth 2.0 with the user's identity) rather than a shared maker connection. +3. In ServiceNow, make sure the OAuth application registry entry allows the Entra ID users + who will use the agent, and that those users have read access to `sc_request`. +4. Each employee is prompted to sign in to ServiceNow **once**, on first use. + +Confirm it worked: `workflow.json` should show `"runtimeSource": "invoker"` on the +ServiceNow connection reference after deployment. If it flips to `"embedded"`, the flow is +running as the maker and every user will see the maker's data โ€” reconfigure before shipping. + +> **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's +> `connectionReferences` block (the connection key typically becomes `shared_service-now-1`). +> If you later re-export or re-push the flow, re-check that block so you don't overwrite the +> live OBO binding with a stale one. --- diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md index 0e1e98da..ff158938 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md @@ -27,27 +27,55 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look ## ๐Ÿš€ Setup +> **โš ๏ธ OBO (on-behalf-of) is required for this flow.** The ServiceNow connection uses +> `"runtimeSource": "invoker"`, so the flow calls ServiceNow **as the signed-in employee**, +> not as the maker. This keeps ServiceNow's row-level ACLs in force. See +> [Configure OBO](#-configure-obo-required) below. + +Install the companion [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) sample **first** โ€” its user-facing topic supplies the RITM routing. + ### Option A โ€” ESS Maker Kit (recommended) -1. Install and set up the companion [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) sample first (its user-facing topic is required for RITM routing to work). -2. Copy the `topics/` file into `workspace/agents/{your-agent-slug}/topics/`. -3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{NEW-GUID}/`. -4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. -5. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). -6. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. -7. Run `python scripts/push.py` to deploy. -8. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector with `runtimeSource: invoker`. -9. Run `python scripts/push.py --repair "Get Request Item"` to wire the topicโ†’flow link. -10. Publish the agent in Copilot Studio. +1. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. +2. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{NEW-GUID}/`. +3. Replace the placeholders: + - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 2). + - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. +4. `python scripts/push.py` +5. **Configure OBO** โ€” see below. Do this *before* turning the flow on. +6. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for invoker-auth ServiceNow flows). +7. `python scripts/push.py --repair "Get Request Item"` to wire topic โ†’ flow. +8. Publish the agent in Copilot Studio. ### Option B โ€” Manual -1. In Power Automate, import `workflow/workflow.json` as a new cloud flow. -2. Update the ServiceNow and Dataverse connection references to point to your environment's connections. -3. Turn the flow on. Note the flow's ID (visible in the Power Automate URL). -4. In Copilot Studio, open your ESS agent and create a new system topic using the YAML in `topics/` as the content โ€” replacing `{FLOW_GUID}` with the flow ID from step 3. -5. Make sure the `servicenow-itsm-get-request-details` topic (from the companion sample) is installed and its `BeginDialog` reference to `ess-hr-servicenow-itsm-system-get-request-item` resolves to this new system topic. -6. Publish. +1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. +2. Point the ServiceNow and Dataverse connection references at your environment's connections. +3. **Configure OBO** โ€” see below. +4. Turn the flow on and copy its ID from the URL. +5. In Copilot Studio, create the system topic from `topics/`, replacing `{FLOW_GUID}` with that ID. +6. Confirm the companion topic's `BeginDialog` reference to `ess-hr-servicenow-itsm-system-get-request-item` resolves. +7. Publish. + +### ๐Ÿ” Configure OBO (required) + +The ServiceNow connector must authenticate as the end user, not the maker: + +1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. +2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user** + rather than a shared maker connection. +3. In ServiceNow, ensure the OAuth application registry entry allows your Entra ID users, + and that they have read access to `sc_req_item`. +4. Each employee signs in to ServiceNow **once**, on first use. + +Confirm it worked: `workflow.json` should show `"runtimeSource": "invoker"` on the +ServiceNow connection reference after deployment. If it reads `"embedded"`, the flow is +running as the maker โ€” reconfigure before shipping. + +> **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's +> `connectionReferences` block (the key typically becomes `shared_service-now-1`). If you +> re-export or re-push the flow later, re-check that block so you don't overwrite the live +> OBO binding with a stale one. --- diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 26591633..c11aa24d 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -64,35 +64,66 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list ## ๐Ÿš€ Setup +> **โš ๏ธ Read the OBO note first.** Unlike the other ServiceNow samples in this collection, +> this flow ships with `"runtimeSource": "embedded"` โ€” it calls ServiceNow as the **maker**, +> and relies on a `requested_for.email` filter for isolation. For most tenants you should +> switch it to OBO. See [Configure OBO](#-configure-obo-strongly-recommended) below. + ### Option A โ€” ESS Maker Kit (recommended) -1. Open your ESS Maker Kit workspace and run `/setup` to connect to the target environment. -2. Copy the `topics/` files into `workspace/agents/{your-agent-slug}/topics/`. -3. Copy the `workflow/` folder into `workspace/agents/{your-agent-slug}/workflows/ess-hr-servicenow-itsm-get-user-requests-{NEW-GUID}/`. -4. In **the system topic**, replace `{FLOW_GUID}` with the same GUID you used for the workflow folder name. -5. In **workflow.json**, replace `{SERVICENOW_CONNREF}` and `{DATAVERSE_CONNREF}` with your environment's connection reference logical names (find these in `workspace/agents/{slug}/connectionreferences.mcs.yml`). -6. In **workflow/metadata.yml**, replace `{FLOW_GUID}` with your chosen GUID. -7. Run `python scripts/push.py` to deploy. -8. Manually **turn on** the flow in [Power Automate](https://make.powerautomate.com) โ€” activation via API is blocked for flows that use the ServiceNow connector. -9. Run `python scripts/push.py --repair "Get User Requests"` to wire the topicโ†’flow link. -10. Publish the agent in Copilot Studio. +1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. +2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. +3. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-user-requests-{NEW-GUID}/`. +4. Replace the placeholders: + - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 3). + - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. +5. `python scripts/push.py` +6. **Decide on OBO vs embedded** โ€” see below. Do this *before* turning the flow on. +7. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for ServiceNow flows). +8. `python scripts/push.py --repair "Get User Requests"` to wire topic โ†’ flow. +9. Publish the agent in Copilot Studio. ### Option B โ€” Manual -1. In Power Automate, create a new cloud flow. Add a **Copilot Studio โ†’ When Copilot Studio calls a flow** trigger with inputs: `UserIdentifier` (Text) and `IsActive` (Text). -2. Add a **ServiceNow โ†’ List Records** action on the `sc_request` table. Set the filter query to build from the inputs (active/inactive/all based on `IsActive`), select fields `number,short_description,request_state,requested_for,assigned_to,sys_updated_on,sys_id,price`, limit 10. -3. Add the **Dataverse โ†’ Perform an unbound action** step calling `msdyn_ServiceNowOutputFieldMapperPlugin` to rename the fields. -4. Return the mapped output via a **Respond to Copilot** action. -5. Note the flow ID and update the system topic's `flowId` field. -6. In Copilot Studio, open your ESS agent and create two new topics using the YAML in `topics/` โ€” replacing `{FLOW_GUID}` with the flow ID from step 5. -7. Publish. +1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. +2. Point the ServiceNow and Dataverse connection references at your environment's connections. +3. **Decide on OBO vs embedded** โ€” see below. +4. Turn the flow on and copy its ID from the URL. +5. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. +6. Publish. + +### ๐Ÿ” Configure OBO (strongly recommended) + +**As shipped (`embedded`):** the flow uses the maker's ServiceNow connection. Users are +isolated only by the `requested_for.email = UserIdentifier` filter in the query โ€” ServiceNow's +own ACLs are evaluated against the *maker's* account. That means the maker needs broad read +access to `sc_request`, and a bug or prompt-injection in the filter could expose other users' data. + +**Switch to OBO (`invoker`)** so ServiceNow enforces per-user access itself: + +1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. +2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user**. +3. In ServiceNow, ensure the OAuth application registry entry allows your Entra ID users, + and that they have read access to `sc_request`. +4. Confirm `workflow.json` shows `"runtimeSource": "invoker"` on the ServiceNow connection + reference after deployment. +5. Keep the `requested_for.email` filter โ€” with OBO it becomes defence-in-depth rather than + the sole isolation boundary. + +Trade-off: OBO prompts each employee to sign in to ServiceNow once, and the 15-minute cache +becomes per-user. Stay on `embedded` only if every agent user is meant to see the same data. + +> **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's +> `connectionReferences` block (the key typically becomes `shared_service-now-1`). If you +> re-export or re-push the flow later, re-check that block so you don't overwrite the live +> OBO binding with a stale one. --- ## โš ๏ธ Known Limitations - **Activation via API fails** โ€” same as other ServiceNow standalone flows in this collection. Manual turn-on in Power Automate is required. -- **Embedded connection** โ€” the list flow uses `runtimeSource: embedded` (maker's connection), not the invoker's credentials. The filter `requested_for.email = UserIdentifier` ensures each user only sees their own requests, but ServiceNow row-level ACLs are evaluated against the maker's account. Ensure the maker's ServiceNow account has read access to `sc_request`. +- **Embedded connection by default** โ€” as shipped the flow runs as the maker, not the invoker. See [Configure OBO](#-configure-obo-strongly-recommended) for the trade-off and how to switch. - **Global cache variables** โ€” the topic uses `Global.ESS_ServiceNow_AllRequests` and `Global.ESS_ServiceNow_NextAllRequestsRefresh`. These are agent-scoped global variables โ€” ensure they don't conflict with other topics in your agent. - **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Request_Records` **Select** step normalizes these to plain display-value strings across every row before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. - **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise this flow. From c40712607ff1a192c1ae647497117fa36ed70b62 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:31:37 -0400 Subject: [PATCH 11/20] refactor(ess): merge RITM sample into get-request-details; restore frontmatter attribution The user-facing topic routes both REQ and RITM, so the two folders were mutually dependent and neither installed cleanly on its own. Consolidates into one sample with a workflows/ folder per flow. Also restores the YAML frontmatter (incl. author) dropped from the get-request-details README in fc2c793. --- .../README.md | 108 +++++++++++++----- ...cenow-itsm-system-get-request-item.mcs.yml | 0 .../get-request-details}/metadata.yml | 0 .../get-request-details}/workflow.json | 0 .../workflows/get-request-item}/metadata.yml | 0 .../workflows/get-request-item}/workflow.json | 0 .../README.md | 86 -------------- .../README.md | 6 +- 8 files changed, 85 insertions(+), 115 deletions(-) rename copilot-agent-samples/ESS/{servicenow-itsm-get-request-item => servicenow-itsm-get-request-details}/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml (100%) rename copilot-agent-samples/ESS/servicenow-itsm-get-request-details/{workflow => workflows/get-request-details}/metadata.yml (100%) rename copilot-agent-samples/ESS/servicenow-itsm-get-request-details/{workflow => workflows/get-request-details}/workflow.json (100%) rename copilot-agent-samples/ESS/{servicenow-itsm-get-request-item/workflow => servicenow-itsm-get-request-details/workflows/get-request-item}/metadata.yml (100%) rename copilot-agent-samples/ESS/{servicenow-itsm-get-request-item/workflow => servicenow-itsm-get-request-details/workflows/get-request-item}/workflow.json (100%) delete mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 96f06a1f..07fac7f8 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -1,18 +1,62 @@ -# ESS ServiceNow ITSM โ€“ Get Request Details +--- +title: ESS ServiceNow ITSM โ€“ Get Request Details (REQ + RITM) +type: extension +category: Copilot Studio / Employee Self-Service +summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees look up the status and details of a ServiceNow service catalog request (REQ) or request item (RITM) via natural language. +author: + - Dean Cron +version: 1.1.0 +published: "2026-07-30" +tags: + - ESS + - Employee Self-Service + - ServiceNow + - ITSM + - service catalog + - sc_request + - sc_req_item +format: extension +whatItIs: >- + A three-topic + two-flow extension for the Microsoft Employee Self-Service + (ESS) Copilot Studio agent. A single user-facing topic recognises both service + catalog requests (REQ prefix) and request items (RITM prefix), routes each to + the matching flow, and returns a rich adaptive card showing the number, state, + description, assignment, and a direct link to ServiceNow. +whyUseIt: + - The ESS shared ITSM orchestrator flow only supports incident (INC) lookups. + This extension adds sc_request (REQ) and sc_req_item (RITM) support without + modifying any shared flows. + - One topic covers both record types, so employees don't need to know whether + their number is a REQ or a RITM. + - Follows the ESS two-topic pattern (user-facing + system routing) for + consistency with out-of-the-box ESS topics. + - Uses the same msdyn_ServiceNowOutputFieldMapperPlugin for field renaming, + keeping the response shape compatible with standard ESS adaptive card patterns. +howToUse: |- + See the Setup section below. +prerequisites: + - Microsoft Employee Self-Service agent (deployed) + - ServiceNow ITSM extension pack installed in Copilot Studio + - An active ServiceNow connection in Power Platform configured for on-behalf-of + (invoker) auth โ€” see the Configure OBO section + - ESS Maker Kit (for automated deployment via push.py) +--- + +# ESS ServiceNow ITSM โ€“ Get Request Details (REQ + RITM) ## ๐Ÿ“Œ Overview -Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog requests** (REQ numbers) from ServiceNow. The built-in ESS ITSM orchestrator only handles incidents (INC). This sample adds a standalone flow that queries the `sc_request` table directly. +Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog requests** (REQ) and **request items** (RITM) from ServiceNow. The built-in ESS ITSM orchestrator only handles incidents (INC), so this sample adds two standalone flows โ€” one for `sc_request`, one for `sc_req_item`. -The user-facing topic also **routes RITM (request item) numbers** to the companion [`servicenow-itsm-get-request-item`](../servicenow-itsm-get-request-item/) sample โ€” install that sample too if you want RITM lookups to work. Without it, RITM input is still recognized but the routed dialog reference won't resolve. +A **single user-facing topic** handles both. It extracts the identifier from the user's message and routes RITM-prefixed numbers to the request-item flow and everything else to the request flow. **Example conversation:** > User: "What's the status of my request REQ0012345?" -> Agent: *[Adaptive card showing request number, state, description, assigned to, last updated, and a link to open in ServiceNow]* +> Agent: *[Adaptive card showing request number, state, description, requested for, assigned to, last updated, and a link to open in ServiceNow]* > User: "Look up RITM0001234" -> Agent: *[Adaptive card showing request item number, state, catalog item, parent request, assigned to, and a link to open in ServiceNow โ€” requires the companion `servicenow-itsm-get-request-item` sample]* +> Agent: *[Adaptive card showing request item number, state, catalog item, parent request, assigned to, quantity, and a link to open in ServiceNow]* --- @@ -20,10 +64,15 @@ The user-facing topic also **routes RITM (request item) numbers** to the compani | File | Purpose | |------|---------| -| `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, REQ/RITM routing, adaptive card | -| `topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” routes to the standalone flow via `InvokeFlowAction` | -| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_request` by REQ number | -| `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | +| `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, REQ/RITM routing, both adaptive cards | +| `topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” invokes the REQ flow | +| `topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` | System topic โ€” invokes the RITM flow | +| `workflows/get-request-details/` | Power Automate flow โ€” queries `sc_request` by REQ number | +| `workflows/get-request-item/` | Power Automate flow โ€” queries `sc_req_item` by RITM number | + +Each `workflows/*` folder contains a `workflow.json` and a `metadata.yml`. + +> **Install all of it.** The user-facing topic references *both* system topics, so the pieces are not independently installable โ€” deploy both flows and all three topics together. --- @@ -36,25 +85,30 @@ The user-facing topic also **routes RITM (request item) numbers** to the compani ### Option A โ€” ESS Maker Kit (recommended) +This sample ships **two** flows. Generate a separate GUID for each and repeat the copy/replace for both. + 1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. -2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. -3. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{NEW-GUID}/`. -4. Replace the placeholders: - - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 3). - - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. +2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/` (all three topic files). +3. Copy each flow into its own workspace folder: + - `workflows/get-request-details/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{GUID-A}/` + - `workflows/get-request-item/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{GUID-B}/` +4. Replace the placeholders **in each flow folder and its matching system topic**: + - `{FLOW_GUID}` โ†’ `{GUID-A}` in `ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` and `get-request-details/metadata.yml`. + - `{FLOW_GUID}` โ†’ `{GUID-B}` in `ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` and `get-request-item/metadata.yml`. + - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in both `workflow.json` files, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. 5. `python scripts/push.py` -6. **Configure OBO** โ€” see below. Do this *before* turning the flow on. -7. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for invoker-auth ServiceNow flows). -8. `python scripts/push.py --repair "Get Request Details"` to wire topic โ†’ flow. +6. **Configure OBO** โ€” see below. Do this *before* turning the flows on. +7. Turn **both flows** on in [Power Automate](https://make.powerautomate.com) (API activation is blocked for invoker-auth ServiceNow flows). +8. `python scripts/push.py --repair "Get Request Details"` and `--repair "Get Request Item"` to wire topic โ†’ flow. 9. Publish the agent in Copilot Studio. ### Option B โ€” Manual -1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. +1. Import **both** `workflows/*/workflow.json` files into Power Automate as new cloud flows. 2. Point the ServiceNow and Dataverse connection references at your environment's connections. 3. **Configure OBO** โ€” see below. -4. Turn the flow on and copy its ID from the URL. -5. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. +4. Turn both flows on and copy each flow ID from its URL. +5. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. 6. Publish. ### ๐Ÿ” Configure OBO (required) @@ -65,11 +119,11 @@ The ServiceNow connector must authenticate as the end user, not the maker: 2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user** (OAuth 2.0 with the user's identity) rather than a shared maker connection. 3. In ServiceNow, make sure the OAuth application registry entry allows the Entra ID users - who will use the agent, and that those users have read access to `sc_request`. + who will use the agent, and that those users have read access to `sc_request` and `sc_req_item`. 4. Each employee is prompted to sign in to ServiceNow **once**, on first use. -Confirm it worked: `workflow.json` should show `"runtimeSource": "invoker"` on the -ServiceNow connection reference after deployment. If it flips to `"embedded"`, the flow is +Confirm it worked: both `workflow.json` files should show `"runtimeSource": "invoker"` on the +ServiceNow connection reference after deployment. If either flips to `"embedded"`, that flow is running as the maker and every user will see the maker's data โ€” reconfigure before shipping. > **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's @@ -81,7 +135,7 @@ running as the maker and every user will see the maker's data โ€” reconfigure be ## โš ๏ธ Known Limitations -- **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flow is created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 9 above). -- **sc_request fields** โ€” the flow returns `request_state` (not `state`) and `requested_for` (not `caller_id`). The adaptive card handles this, but if you customise the output be aware the field names differ from the incident table. -- **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. -- **RITM lookups require the companion sample** โ€” the user-facing topic recognizes RITM numbers and routes them to `ess-hr-servicenow-itsm-system-get-request-item`, but that system topic and its flow live in the separate `servicenow-itsm-get-request-item` sample. Install both samples together for full REQ + RITM coverage. +- **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flows are created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 7 above). +- **sc_request fields** โ€” the REQ flow returns `request_state` (not `state`) and `requested_for` (not `caller_id`). The adaptive card handles this, but if you customise the output be aware the field names differ from the incident table. +- **Reference fields** โ€” `requested_for`, `assigned_to`, `cat_item`, and `request` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). Each flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. +- **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise these flows. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-item/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/metadata.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/workflow.json similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflow/workflow.json rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/workflow.json diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/metadata.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/metadata.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/metadata.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/workflow.json similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-item/workflow/workflow.json rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/workflow.json diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md deleted file mode 100644 index ff158938..00000000 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-item/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# ESS ServiceNow ITSM โ€“ Get Request Item (RITM) - -## ๐Ÿ“Œ Overview - -Extends the Microsoft Employee Self-Service (ESS) agent with the ability to look up **service catalog request items** (RITM numbers) from ServiceNow. This sample is the RITM counterpart to [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) โ€” it adds a standalone flow that queries the `sc_req_item` table directly. - -**This sample depends on `servicenow-itsm-get-request-details`.** The routing logic that decides "is this a REQ or a RITM?" lives in that sample's user-facing topic (`servicenow-itsm-get-request-details.mcs.yml`). Install both samples together โ€” this one only supplies the system topic + flow that the shared topic redirects to for RITM numbers. - -**Example conversation:** - -> User: "Look up RITM0001234" -> Agent: *[Adaptive card showing request item number, state, catalog item, parent request, assigned to, quantity, last updated, and a link to open in ServiceNow]* - ---- - -## ๐Ÿ“ Files - -| File | Purpose | -|------|---------| -| `topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` | System topic โ€” routes to the standalone flow via `InvokeFlowAction` | -| `workflow/workflow.json` | Standalone Power Automate flow โ€” queries `sc_req_item` by RITM number | -| `workflow/metadata.yml` | Flow metadata for the ESS Maker Kit push script | - -> Note: there is no separate user-facing topic in this sample. RITM lookups are triggered by `servicenow-itsm-get-request-details.mcs.yml` (installed as part of the companion sample), which recognizes RITM-prefixed identifiers and redirects to the system topic here. - ---- - -## ๐Ÿš€ Setup - -> **โš ๏ธ OBO (on-behalf-of) is required for this flow.** The ServiceNow connection uses -> `"runtimeSource": "invoker"`, so the flow calls ServiceNow **as the signed-in employee**, -> not as the maker. This keeps ServiceNow's row-level ACLs in force. See -> [Configure OBO](#-configure-obo-required) below. - -Install the companion [`servicenow-itsm-get-request-details`](../servicenow-itsm-get-request-details/) sample **first** โ€” its user-facing topic supplies the RITM routing. - -### Option A โ€” ESS Maker Kit (recommended) - -1. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. -2. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{NEW-GUID}/`. -3. Replace the placeholders: - - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 2). - - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. -4. `python scripts/push.py` -5. **Configure OBO** โ€” see below. Do this *before* turning the flow on. -6. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for invoker-auth ServiceNow flows). -7. `python scripts/push.py --repair "Get Request Item"` to wire topic โ†’ flow. -8. Publish the agent in Copilot Studio. - -### Option B โ€” Manual - -1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. -2. Point the ServiceNow and Dataverse connection references at your environment's connections. -3. **Configure OBO** โ€” see below. -4. Turn the flow on and copy its ID from the URL. -5. In Copilot Studio, create the system topic from `topics/`, replacing `{FLOW_GUID}` with that ID. -6. Confirm the companion topic's `BeginDialog` reference to `ess-hr-servicenow-itsm-system-get-request-item` resolves. -7. Publish. - -### ๐Ÿ” Configure OBO (required) - -The ServiceNow connector must authenticate as the end user, not the maker: - -1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. -2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user** - rather than a shared maker connection. -3. In ServiceNow, ensure the OAuth application registry entry allows your Entra ID users, - and that they have read access to `sc_req_item`. -4. Each employee signs in to ServiceNow **once**, on first use. - -Confirm it worked: `workflow.json` should show `"runtimeSource": "invoker"` on the -ServiceNow connection reference after deployment. If it reads `"embedded"`, the flow is -running as the maker โ€” reconfigure before shipping. - -> **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's -> `connectionReferences` block (the key typically becomes `shared_service-now-1`). If you -> re-export or re-push the flow later, re-check that block so you don't overwrite the live -> OBO binding with a stale one. - ---- - -## โš ๏ธ Known Limitations - -- **Activation via API fails** โ€” when deploying via the Maker Kit push script, the flow is created but cannot be activated automatically. Power Platform validates the ServiceNow connection schema at activation time, which requires a live user session. Manual turn-on in Power Automate is required (step 8 above). -- **Requires the companion sample** โ€” this sample has no standalone trigger phrases of its own. Without `servicenow-itsm-get-request-details` installed, RITM input will not be routed here. -- **Reference fields** โ€” `cat_item`, `request`, and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index c11aa24d..1b356340 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -5,7 +5,7 @@ category: Copilot Studio / Employee Self-Service summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees list their ServiceNow service catalog requests (REQ numbers) via natural language. author: - Dean Cron -version: 1.0.0 +version: 1.0.1 published: "2026-07-30" tags: - ESS @@ -31,7 +31,9 @@ howToUse: |- prerequisites: - Microsoft Employee Self-Service agent (deployed) - ServiceNow ITSM extension pack installed in Copilot Studio - - An active ServiceNow connection in Power Platform (maker's embedded connection) + - An active ServiceNow connection in Power Platform (ships as the maker's + embedded connection; on-behalf-of / invoker auth is strongly recommended โ€” + see the Configure OBO section) - ESS Maker Kit (for automated deployment via push.py) --- From 886b13d3ab720c529ed0c8c151f38ec0aa9865f3 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:40:17 -0400 Subject: [PATCH 12/20] docs(ess): drop unused catalog front matter from ESS sample READMEs copilot-agent-samples/ESS is not in the catalog builder's folderRoots (tools/catalog-build/index.js), so the YAML front matter in these READMEs was never parsed and rendered as a visible table on GitHub. Its type/format values ('extension') were also outside the builder's allowedTypes/allowedFormats, so it would have failed validation had the root ever been added. Move the prerequisites, which existed only in front matter, into a visible Prerequisites section in each README so the content is not lost. Add a human-readable Author table per TEMPLATE-README.md. --- .../README.md | 61 ++++++------------- .../README.md | 56 ++++++----------- 2 files changed, 34 insertions(+), 83 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 07fac7f8..1472ea3c 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -1,47 +1,3 @@ ---- -title: ESS ServiceNow ITSM โ€“ Get Request Details (REQ + RITM) -type: extension -category: Copilot Studio / Employee Self-Service -summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees look up the status and details of a ServiceNow service catalog request (REQ) or request item (RITM) via natural language. -author: - - Dean Cron -version: 1.1.0 -published: "2026-07-30" -tags: - - ESS - - Employee Self-Service - - ServiceNow - - ITSM - - service catalog - - sc_request - - sc_req_item -format: extension -whatItIs: >- - A three-topic + two-flow extension for the Microsoft Employee Self-Service - (ESS) Copilot Studio agent. A single user-facing topic recognises both service - catalog requests (REQ prefix) and request items (RITM prefix), routes each to - the matching flow, and returns a rich adaptive card showing the number, state, - description, assignment, and a direct link to ServiceNow. -whyUseIt: - - The ESS shared ITSM orchestrator flow only supports incident (INC) lookups. - This extension adds sc_request (REQ) and sc_req_item (RITM) support without - modifying any shared flows. - - One topic covers both record types, so employees don't need to know whether - their number is a REQ or a RITM. - - Follows the ESS two-topic pattern (user-facing + system routing) for - consistency with out-of-the-box ESS topics. - - Uses the same msdyn_ServiceNowOutputFieldMapperPlugin for field renaming, - keeping the response shape compatible with standard ESS adaptive card patterns. -howToUse: |- - See the Setup section below. -prerequisites: - - Microsoft Employee Self-Service agent (deployed) - - ServiceNow ITSM extension pack installed in Copilot Studio - - An active ServiceNow connection in Power Platform configured for on-behalf-of - (invoker) auth โ€” see the Configure OBO section - - ESS Maker Kit (for automated deployment via push.py) ---- - # ESS ServiceNow ITSM โ€“ Get Request Details (REQ + RITM) ## ๐Ÿ“Œ Overview @@ -76,6 +32,15 @@ Each `workflows/*` folder contains a `workflow.json` and a `metadata.yml`. --- +## โœ… Prerequisites + +- Microsoft Employee Self-Service agent (deployed) +- ServiceNow ITSM extension pack installed in Copilot Studio +- An active ServiceNow connection in Power Platform configured for on-behalf-of (invoker) auth โ€” see [Configure OBO](#-configure-obo-required) +- ESS Maker Kit (for automated deployment via `push.py`) + +--- + ## ๐Ÿš€ Setup > **โš ๏ธ OBO (on-behalf-of) is required for this flow.** The ServiceNow connection uses @@ -139,3 +104,11 @@ running as the maker and every user will see the maker's data โ€” reconfigure be - **sc_request fields** โ€” the REQ flow returns `request_state` (not `state`) and `requested_for` (not `caller_id`). The adaptive card handles this, but if you customise the output be aware the field names differ from the incident table. - **Reference fields** โ€” `requested_for`, `assigned_to`, `cat_item`, and `request` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). Each flow's `Flatten_Reference_Fields` step normalizes these to plain display-value strings before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. - **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise these flows. + +--- + +## Author + +| Author | Original Publish Date | +| --- | --- | +| Dean Cron | 2026-07-30 | diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 1b356340..5cae90f4 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -1,42 +1,3 @@ ---- -title: ESS ServiceNow ITSM โ€“ Get User Requests -type: extension -category: Copilot Studio / Employee Self-Service -summary: Adds a topic to the Microsoft Employee Self-Service (ESS) agent that lets employees list their ServiceNow service catalog requests (REQ numbers) via natural language. -author: - - Dean Cron -version: 1.0.1 -published: "2026-07-30" -tags: - - ESS - - Employee Self-Service - - ServiceNow - - ITSM - - service catalog - - sc_request -format: extension -whatItIs: >- - A two-topic + standalone flow extension for the Microsoft Employee Self-Service - (ESS) Copilot Studio agent. Employees can ask the agent for a list of their - service catalog requests โ€” filtered by state (active, inactive, or all) โ€” and - receive a numbered list with direct ServiceNow links. -whyUseIt: - - The ESS shared ITSM orchestrator flow only supports listing incidents (INC). - This extension adds sc_request (REQ) list support without modifying any shared flows. - - Includes a 15-minute cache for "all requests" queries (matching the Get User Tickets pattern). - - Pairs naturally with the ESS ServiceNow ITSM โ€“ Get Request Details sample for - full REQ request management. -howToUse: |- - See the Setup section below. -prerequisites: - - Microsoft Employee Self-Service agent (deployed) - - ServiceNow ITSM extension pack installed in Copilot Studio - - An active ServiceNow connection in Power Platform (ships as the maker's - embedded connection; on-behalf-of / invoker auth is strongly recommended โ€” - see the Configure OBO section) - - ESS Maker Kit (for automated deployment via push.py) ---- - # ESS ServiceNow ITSM โ€“ Get User Requests ## ๐Ÿ“Œ Overview @@ -64,6 +25,15 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list --- +## โœ… Prerequisites + +- Microsoft Employee Self-Service agent (deployed) +- ServiceNow ITSM extension pack installed in Copilot Studio +- An active ServiceNow connection in Power Platform โ€” this sample ships with the maker's `embedded` connection; on-behalf-of (`invoker`) auth is strongly recommended, see [Configure OBO](#-configure-obo-strongly-recommended) +- ESS Maker Kit (for automated deployment via `push.py`) + +--- + ## ๐Ÿš€ Setup > **โš ๏ธ Read the OBO note first.** Unlike the other ServiceNow samples in this collection, @@ -129,3 +99,11 @@ becomes per-user. Stay on `embedded` only if every agent user is meant to see th - **Global cache variables** โ€” the topic uses `Global.ESS_ServiceNow_AllRequests` and `Global.ESS_ServiceNow_NextAllRequestsRefresh`. These are agent-scoped global variables โ€” ensure they don't conflict with other topics in your agent. - **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Request_Records` **Select** step normalizes these to plain display-value strings across every row before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. - **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise this flow. + +--- + +## Author + +| Author | Original Publish Date | +| --- | --- | +| Dean Cron | 2026-07-30 | From 2a3a9e06f66e388d10619b3a91c234b0c703bcc5 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:48:54 -0400 Subject: [PATCH 13/20] docs(ess): correct OBO setup steps to use connection parameter sharing The previous steps described a Settings > Generative AI / Connections path with a 'Sign in on behalf of the user' option, which does not exist. OBO is enabled by turning on 'Allow permission to share parameters' under Settings > Connection Settings > See details > Connection parameters, and selecting the parameters to share. Call out that the toggle must be set for each flow's connection entry, link the Copilot Studio docs, and reword the end-user effect as a permission consent prompt rather than a ServiceNow sign-in. --- .../README.md | 25 +++++++++----- .../README.md | 34 ++++++++++++------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 1472ea3c..f0febb41 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -78,14 +78,23 @@ This sample ships **two** flows. Generate a separate GUID for each and repeat th ### ๐Ÿ” Configure OBO (required) -The ServiceNow connector must authenticate as the end user, not the maker: - -1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. -2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user** - (OAuth 2.0 with the user's identity) rather than a shared maker connection. -3. In ServiceNow, make sure the OAuth application registry entry allows the Entra ID users - who will use the agent, and that those users have read access to `sc_request` and `sc_req_item`. -4. Each employee is prompted to sign in to ServiceNow **once**, on first use. +Both flows must call ServiceNow as the signed-in employee. Enable parameter sharing on the +ServiceNow connection backing **each** flow: + +1. In [Copilot Studio](https://copilotstudio.microsoft.com/), select **Agents** and open your agent. +2. Select **Settings**, then **Connection Settings**. +3. Find the **ServiceNow** connection row. Under **Manage**, select **See details**. +4. Open the **Connection parameters** tab. +5. Turn on **Allow permission to share parameters**. +6. Select the checkboxes for the parameters you want the user to share. +7. Repeat for the connection entry of **each** flow (Get Request Details *and* Get Request Item). + +Each employee is prompted to grant permission the first time the agent uses the connection on +their behalf. Full reference: +[Share connection parameters for On-Behalf-Of (OBO) authentication](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-connections#share-connection-parameters-for-on-behalf-of-obo-authentication). + +You also need the ServiceNow side to permit those users โ€” the OAuth application registry entry +must allow your Entra ID users, and they need read access to `sc_request` and `sc_req_item`. Confirm it worked: both `workflow.json` files should show `"runtimeSource": "invoker"` on the ServiceNow connection reference after deployment. If either flips to `"embedded"`, that flow is diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 5cae90f4..cc748ef6 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -71,18 +71,28 @@ isolated only by the `requested_for.email = UserIdentifier` filter in the query own ACLs are evaluated against the *maker's* account. That means the maker needs broad read access to `sc_request`, and a bug or prompt-injection in the filter could expose other users' data. -**Switch to OBO (`invoker`)** so ServiceNow enforces per-user access itself: - -1. In **Copilot Studio**, open your agent โ†’ **Settings** โ†’ **Generative AI / Connections**. -2. Select the **ServiceNow** connection and choose **Sign in on behalf of the user**. -3. In ServiceNow, ensure the OAuth application registry entry allows your Entra ID users, - and that they have read access to `sc_request`. -4. Confirm `workflow.json` shows `"runtimeSource": "invoker"` on the ServiceNow connection - reference after deployment. -5. Keep the `requested_for.email` filter โ€” with OBO it becomes defence-in-depth rather than - the sole isolation boundary. - -Trade-off: OBO prompts each employee to sign in to ServiceNow once, and the 15-minute cache +**Switch to OBO (`invoker`)** so ServiceNow enforces per-user access itself. Enable parameter +sharing on the ServiceNow connection backing this flow: + +1. In [Copilot Studio](https://copilotstudio.microsoft.com/), select **Agents** and open your agent. +2. Select **Settings**, then **Connection Settings**. +3. Find the **ServiceNow** connection row. Under **Manage**, select **See details**. +4. Open the **Connection parameters** tab. +5. Turn on **Allow permission to share parameters**. +6. Select the checkboxes for the parameters you want the user to share. + +Each employee is prompted to grant permission the first time the agent uses the connection on +their behalf. Full reference: +[Share connection parameters for On-Behalf-Of (OBO) authentication](https://learn.microsoft.com/en-us/microsoft-copilot-studio/authoring-connections#share-connection-parameters-for-on-behalf-of-obo-authentication). + +On the ServiceNow side, ensure the OAuth application registry entry allows your Entra ID users +and that they have read access to `sc_request`. + +Afterwards, confirm `workflow.json` shows `"runtimeSource": "invoker"` on the ServiceNow +connection reference. Keep the `requested_for.email` filter โ€” with OBO it becomes +defence-in-depth rather than the sole isolation boundary. + +Trade-off: OBO prompts each employee to grant permission once, and the 15-minute cache becomes per-user. Stay on `embedded` only if every agent user is meant to see the same data. > **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's From 01ddfbb8f207cf2ebcb76fb5c017ef6585c17989 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 10:55:16 -0400 Subject: [PATCH 14/20] fix(ess): ship Get User Requests with invoker auth instead of embedded The flow previously ran as the maker, leaving the requested_for.email query filter as the only thing separating one employee's requests from another's. ServiceNow ACLs were evaluated against the maker account, so a fault in the filter would expose other users' data. Switching runtimeSource to invoker makes ServiceNow enforce per-user access itself, matching the other ServiceNow samples in this collection. Update the README accordingly: OBO is now required rather than optional, drop the embedded-vs-invoker decision from the setup steps, and replace the 'embedded by default' limitation with a note that the 15-minute cache is per-user under OBO. --- .../README.md | 37 ++++++++----------- .../workflow/workflow.json | 2 +- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index cc748ef6..4780ed76 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -29,17 +29,17 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list - Microsoft Employee Self-Service agent (deployed) - ServiceNow ITSM extension pack installed in Copilot Studio -- An active ServiceNow connection in Power Platform โ€” this sample ships with the maker's `embedded` connection; on-behalf-of (`invoker`) auth is strongly recommended, see [Configure OBO](#-configure-obo-strongly-recommended) +- An active ServiceNow connection in Power Platform configured for on-behalf-of (invoker) auth โ€” see [Configure OBO](#-configure-obo-required) - ESS Maker Kit (for automated deployment via `push.py`) --- ## ๐Ÿš€ Setup -> **โš ๏ธ Read the OBO note first.** Unlike the other ServiceNow samples in this collection, -> this flow ships with `"runtimeSource": "embedded"` โ€” it calls ServiceNow as the **maker**, -> and relies on a `requested_for.email` filter for isolation. For most tenants you should -> switch it to OBO. See [Configure OBO](#-configure-obo-strongly-recommended) below. +> **โš ๏ธ OBO (on-behalf-of) is required for this flow.** The ServiceNow connection uses +> `"runtimeSource": "invoker"`, so the flow calls ServiceNow **as the signed-in employee**, +> not as the maker. This keeps ServiceNow's own row-level ACLs in force โ€” each user only sees +> requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. ### Option A โ€” ESS Maker Kit (recommended) @@ -50,7 +50,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 3). - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. 5. `python scripts/push.py` -6. **Decide on OBO vs embedded** โ€” see below. Do this *before* turning the flow on. +6. **Configure OBO** โ€” see below. Do this *before* turning the flow on. 7. Turn the flow **on** in [Power Automate](https://make.powerautomate.com) (API activation is blocked for ServiceNow flows). 8. `python scripts/push.py --repair "Get User Requests"` to wire topic โ†’ flow. 9. Publish the agent in Copilot Studio. @@ -59,20 +59,15 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list 1. Import `workflow/workflow.json` into Power Automate as a new cloud flow. 2. Point the ServiceNow and Dataverse connection references at your environment's connections. -3. **Decide on OBO vs embedded** โ€” see below. +3. **Configure OBO** โ€” see below. 4. Turn the flow on and copy its ID from the URL. 5. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. 6. Publish. -### ๐Ÿ” Configure OBO (strongly recommended) +### ๐Ÿ” Configure OBO (required) -**As shipped (`embedded`):** the flow uses the maker's ServiceNow connection. Users are -isolated only by the `requested_for.email = UserIdentifier` filter in the query โ€” ServiceNow's -own ACLs are evaluated against the *maker's* account. That means the maker needs broad read -access to `sc_request`, and a bug or prompt-injection in the filter could expose other users' data. - -**Switch to OBO (`invoker`)** so ServiceNow enforces per-user access itself. Enable parameter -sharing on the ServiceNow connection backing this flow: +The flow must call ServiceNow as the signed-in employee so ServiceNow enforces per-user access +itself. Enable parameter sharing on the ServiceNow connection backing this flow: 1. In [Copilot Studio](https://copilotstudio.microsoft.com/), select **Agents** and open your agent. 2. Select **Settings**, then **Connection Settings**. @@ -88,12 +83,12 @@ their behalf. Full reference: On the ServiceNow side, ensure the OAuth application registry entry allows your Entra ID users and that they have read access to `sc_request`. -Afterwards, confirm `workflow.json` shows `"runtimeSource": "invoker"` on the ServiceNow -connection reference. Keep the `requested_for.email` filter โ€” with OBO it becomes -defence-in-depth rather than the sole isolation boundary. +Confirm it worked: `workflow.json` should still show `"runtimeSource": "invoker"` on the +ServiceNow connection reference after deployment. If it flips to `"embedded"`, the flow is +running as the maker โ€” every user would see the maker's data, so reconfigure before shipping. -Trade-off: OBO prompts each employee to grant permission once, and the 15-minute cache -becomes per-user. Stay on `embedded` only if every agent user is meant to see the same data. +The `requested_for.email` filter stays in the query regardless. With OBO it's defence-in-depth +rather than the sole isolation boundary. Note that the 15-minute cache is per-user under OBO. > **Note:** configuring OBO through the Copilot Studio UI rewrites the flow's > `connectionReferences` block (the key typically becomes `shared_service-now-1`). If you @@ -105,7 +100,7 @@ becomes per-user. Stay on `embedded` only if every agent user is meant to see th ## โš ๏ธ Known Limitations - **Activation via API fails** โ€” same as other ServiceNow standalone flows in this collection. Manual turn-on in Power Automate is required. -- **Embedded connection by default** โ€” as shipped the flow runs as the maker, not the invoker. See [Configure OBO](#-configure-obo-strongly-recommended) for the trade-off and how to switch. +- **Per-user cache** โ€” because the flow runs as the invoker, the 15-minute cache holds each employee's own results. It is not shared across users. - **Global cache variables** โ€” the topic uses `Global.ESS_ServiceNow_AllRequests` and `Global.ESS_ServiceNow_NextAllRequestsRefresh`. These are agent-scoped global variables โ€” ensure they don't conflict with other topics in your agent. - **Reference fields** โ€” `requested_for` and `assigned_to` come back from `GetRecords` as `{display_value, link}` objects when populated (or an empty string when not set). The flow's `Flatten_Request_Records` **Select** step normalizes these to plain display-value strings across every row before mapping โ€” if you add more reference fields to `sysparm_fields`, apply the same flatten pattern. - **Result key** โ€” the ServiceNow connector returns rows under `result` (ServiceNow Table API v2), *not* OData's `value`. Feeding `body(...)?['value']` to the output mapper yields `null` and fails with `A null value was found for the property named 'ServiceNowTableData'`. Keep the `?['result']` references intact if you customise this flow. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json index dabbdd61..4debfebe 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json @@ -9,7 +9,7 @@ "connection": { "connectionReferenceLogicalName": "{SERVICENOW_CONNREF}" }, - "runtimeSource": "embedded" + "runtimeSource": "invoker" }, "shared_commondataserviceforapps": { "api": { From 7563a83c0dbdf5c9dbeba58a9bc622a6c518d342 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 12:55:03 -0400 Subject: [PATCH 15/20] feat(ess): ship ServiceNow ITSM flows as importable packages Add legacy import packages (.zip) alongside the existing workflow.json for all three ServiceNow ITSM sample flows, and rewrite Option B in both READMEs to import the package and remap connections instead of hand-wiring connection references. All three packages declare the ServiceNow connection as source=Invoker so imported flows are wired for OBO up front. The Get Request Item package was assembled to match the other two, whose export already carried the Invoker binding. --- .../README.md | 26 ++++++++++++------ .../get-request-details.zip | Bin 0 -> 4112 bytes .../get-request-item/get-request-item.zip | Bin 0 -> 4343 bytes .../README.md | 21 ++++++++------ .../workflow/get-user-requests.zip | Bin 0 -> 4203 bytes 5 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index f0febb41..b6e82588 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -26,7 +26,13 @@ A **single user-facing topic** handles both. It extracts the identifier from the | `workflows/get-request-details/` | Power Automate flow โ€” queries `sc_request` by REQ number | | `workflows/get-request-item/` | Power Automate flow โ€” queries `sc_req_item` by RITM number | -Each `workflows/*` folder contains a `workflow.json` and a `metadata.yml`. +Each `workflows/*` folder ships the flow in two forms: + +| File | Use it for | +|------|-----------| +| `*.zip` | Importable Power Automate package โ€” **Option B** (manual import) | +| `workflow.json` | Raw flow definition โ€” **Option A** (ESS Maker Kit `push.py`) | +| `metadata.yml` | Flow metadata for the ESS Maker Kit push script | > **Install all of it.** The user-facing topic references *both* system topics, so the pieces are not independently installable โ€” deploy both flows and all three topics together. @@ -67,14 +73,18 @@ This sample ships **two** flows. Generate a separate GUID for each and repeat th 8. `python scripts/push.py --repair "Get Request Details"` and `--repair "Get Request Item"` to wire topic โ†’ flow. 9. Publish the agent in Copilot Studio. -### Option B โ€” Manual +### Option B โ€” Manual (import the packages) + +1. In [Power Automate](https://make.powerautomate.com), select **My flows** โ†’ **Import** โ†’ **Import Package (Legacy)**. +2. Upload `workflows/get-request-details/get-request-details.zip`. Repeat the import for `workflows/get-request-item/get-request-item.zip`. +3. For each package, under **Related resources**, select each connection and pick the matching connection in your environment โ€” one **ServiceNow**, one **Microsoft Dataverse**. The display names baked into the package (`SNOW PP2`, `ESS HR`) are from the authoring tenant and are replaced by whatever you select. +4. **Configure OBO** โ€” see below. +5. Turn both flows on and copy each flow ID from its URL. +6. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. +7. Publish. -1. Import **both** `workflows/*/workflow.json` files into Power Automate as new cloud flows. -2. Point the ServiceNow and Dataverse connection references at your environment's connections. -3. **Configure OBO** โ€” see below. -4. Turn both flows on and copy each flow ID from its URL. -5. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. -6. Publish. +> The packages already declare the ServiceNow connection as `"source": "Invoker"`, so an imported +> flow is wired for OBO from the start. You still need the connection-side step below. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip new file mode 100644 index 0000000000000000000000000000000000000000..aacb53b4f183f24b4ac4e21e777d70d5baf8cf24 GIT binary patch literal 4112 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 Q*L26v=VOhGEPtN<1!;`(zyJUM literal 0 HcmV?d00001 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip new file mode 100644 index 0000000000000000000000000000000000000000..fc59af723c75651dc1a2f66c4f9ca25c9333d57f GIT binary patch literal 4343 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 The package already declares the ServiceNow connection as `"source": "Invoker"`, so an imported +> flow is wired for OBO from the start. You still need the connection-side step below. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip new file mode 100644 index 0000000000000000000000000000000000000000..4060aeebd7a0bd41519694d99cb5355e804eb378 GIT binary patch literal 4203 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 literal 0 HcmV?d00001 From 55e6c7bbaaeab64091a629fc48c8da2dc6e7a923 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 13:23:34 -0400 Subject: [PATCH 16/20] docs(ess): link Option A to the ESS Maker Kit repo --- .../ESS/servicenow-itsm-get-request-details/README.md | 4 ++-- .../ESS/servicenow-itsm-get-user-requests/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index b6e82588..c0d2398c 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -43,7 +43,7 @@ Each `workflows/*` folder ships the flow in two forms: - Microsoft Employee Self-Service agent (deployed) - ServiceNow ITSM extension pack installed in Copilot Studio - An active ServiceNow connection in Power Platform configured for on-behalf-of (invoker) auth โ€” see [Configure OBO](#-configure-obo-required) -- ESS Maker Kit (for automated deployment via `push.py`) +- [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (for automated deployment via `push.py`) --- @@ -54,7 +54,7 @@ Each `workflows/*` folder ships the flow in two forms: > not as the maker. This is what keeps ServiceNow's own row-level ACLs in force โ€” each user > only sees requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. -### Option A โ€” ESS Maker Kit (recommended) +### Option A โ€” [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (recommended) This sample ships **two** flows. Generate a separate GUID for each and repeat the copy/replace for both. diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index a0064531..db1327d8 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -31,7 +31,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list - Microsoft Employee Self-Service agent (deployed) - ServiceNow ITSM extension pack installed in Copilot Studio - An active ServiceNow connection in Power Platform configured for on-behalf-of (invoker) auth โ€” see [Configure OBO](#-configure-obo-required) -- ESS Maker Kit (for automated deployment via `push.py`) +- [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (for automated deployment via `push.py`) --- @@ -42,7 +42,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list > not as the maker. This keeps ServiceNow's own row-level ACLs in force โ€” each user only sees > requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. -### Option A โ€” ESS Maker Kit (recommended) +### Option A โ€” [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (recommended) 1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. 2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. From 995b4233c2dc894eaff6824e590ad3b3e808c711 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 16:33:32 -0400 Subject: [PATCH 17/20] ESS ServiceNow ITSM samples: retarget to ESS IT, ship solution packages Rename HR->IT across topics, flow metadata and docs. The dialog: references also move from the msdyn_copilotforemployeeselfservicehr namespace to msdyn_copilotforemployeeselfserviceit; the agent-variant prefix must match the target agent or publish fails. Replace the legacy Power Automate package zips with unmanaged solution zips. The legacy exporter dropped runtimeSource: invoker and embedded an ESS connection reference whose logical name is namespaced per agent variant, so it could never import into a different ESS agent. The solutions carry their own esss_servicenow connection reference, leaving one connection to map at import. Document that topic schema names derive from the filename under push.py but from the display name under the Copilot Studio portal, and that the resulting IncorrectTypeAssignment error is a symptom of the unresolved dialog reference. --- .../README.md | 60 ++++++++++++------ ...erviceNowITSMGetRequestDetails_1_0_0_0.zip | Bin 0 -> 7770 bytes ...w-itsm-system-get-request-details.mcs.yml} | 0 ...enow-itsm-system-get-request-item.mcs.yml} | 0 ...ervicenow-itsm-get-request-details.mcs.yml | 6 +- .../get-request-details.zip | Bin 4112 -> 0 bytes .../get-request-details/metadata.yml | 2 +- .../get-request-item/get-request-item.zip | Bin 4343 -> 0 bytes .../workflows/get-request-item/metadata.yml | 2 +- .../README.md | 50 +++++++++++---- ...SServiceNowITSMGetUserRequests_1_0_0_0.zip | Bin 0 -> 5302 bytes ...servicenow-itsm-get-user-requests.mcs.yml} | 4 +- ...now-itsm-system-get-user-requests.mcs.yml} | 0 .../workflow/get-user-requests.zip | Bin 4203 -> 0 bytes .../workflow/metadata.yml | 2 +- 15 files changed, 87 insertions(+), 39 deletions(-) create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip rename copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/{ess-hr-servicenow-itsm-system-get-request-details.mcs.yml => ess-it-servicenow-itsm-system-get-request-details.mcs.yml} (100%) rename copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/{ess-hr-servicenow-itsm-system-get-request-item.mcs.yml => ess-it-servicenow-itsm-system-get-request-item.mcs.yml} (100%) delete mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip delete mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip create mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip rename copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/{ess-hr-servicenow-itsm-get-user-requests.mcs.yml => ess-it-servicenow-itsm-get-user-requests.mcs.yml} (94%) rename copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/{ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml => ess-it-servicenow-itsm-system-get-user-requests.mcs.yml} (100%) delete mode 100644 copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index c0d2398c..8c81d2ba 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -21,21 +21,41 @@ A **single user-facing topic** handles both. It extracts the identifier from the | File | Purpose | |------|---------| | `topics/servicenow-itsm-get-request-details.mcs.yml` | User-facing topic โ€” trigger phrases, AI input extraction, REQ/RITM routing, both adaptive cards | -| `topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” invokes the REQ flow | -| `topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` | System topic โ€” invokes the RITM flow | +| `topics/ess-it-servicenow-itsm-system-get-request-details.mcs.yml` | System topic โ€” invokes the REQ flow | +| `topics/ess-it-servicenow-itsm-system-get-request-item.mcs.yml` | System topic โ€” invokes the RITM flow | | `workflows/get-request-details/` | Power Automate flow โ€” queries `sc_request` by REQ number | | `workflows/get-request-item/` | Power Automate flow โ€” queries `sc_req_item` by RITM number | +| `solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip` | Unmanaged Power Platform solution containing **both** flows and their ServiceNow connection reference โ€” **Option B** | -Each `workflows/*` folder ships the flow in two forms: +Each `workflows/*` folder ships the flow definition for the push-script path: | File | Use it for | -|------|-----------| -| `*.zip` | Importable Power Automate package โ€” **Option B** (manual import) | +|------|------------| | `workflow.json` | Raw flow definition โ€” **Option A** (ESS Maker Kit `push.py`) | | `metadata.yml` | Flow metadata for the ESS Maker Kit push script | > **Install all of it.** The user-facing topic references *both* system topics, so the pieces are not independently installable โ€” deploy both flows and all three topics together. +> **โš ๏ธ Topic schema names depend on how you install.** The user-facing topic reaches the two +> system topics by schema name, e.g. +> `dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-request-item`. +> How that schema name gets assigned differs by install path: +> +> | Install path | Schema name derived from | Result | +> |---|---|---| +> | **Option A** (`push.py`) | the **filename** | `โ€ฆtopic.ess-it-servicenow-itsm-system-get-request-item` | +> | **Option B** (Copilot Studio portal) | the **display name**, spaces removed | `โ€ฆtopic.ESSITServiceNowITSMSystemGetRequestItem` | +> +> The references in this sample are written for **Option A** and resolve as-is. If you install +> via the portal, update both `dialog:` lines in +> `topics/servicenow-itsm-get-request-details.mcs.yml` to the PascalCase form. A mismatch fails +> at publish with `Dialog with id 'โ€ฆ' not found`, followed by a second, misleading +> `IncorrectTypeAssignment` error (`Assigned: Unspecified, expected: String`) โ€” that one is a +> symptom of the unresolved reference, not a separate problem, and clears with it. +> +> Also note the `msdyn_copilotforemployeeselfserviceit` prefix is the **agent's** schema name. +> These topics target the ESS **IT** agent; retarget the prefix if your agent differs. + --- ## โœ… Prerequisites @@ -61,11 +81,11 @@ This sample ships **two** flows. Generate a separate GUID for each and repeat th 1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. 2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/` (all three topic files). 3. Copy each flow into its own workspace folder: - - `workflows/get-request-details/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-details-{GUID-A}/` - - `workflows/get-request-item/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-request-item-{GUID-B}/` + - `workflows/get-request-details/` โ†’ `workspace/agents/{slug}/workflows/ess-it-servicenow-itsm-get-request-details-{GUID-A}/` + - `workflows/get-request-item/` โ†’ `workspace/agents/{slug}/workflows/ess-it-servicenow-itsm-get-request-item-{GUID-B}/` 4. Replace the placeholders **in each flow folder and its matching system topic**: - - `{FLOW_GUID}` โ†’ `{GUID-A}` in `ess-hr-servicenow-itsm-system-get-request-details.mcs.yml` and `get-request-details/metadata.yml`. - - `{FLOW_GUID}` โ†’ `{GUID-B}` in `ess-hr-servicenow-itsm-system-get-request-item.mcs.yml` and `get-request-item/metadata.yml`. + - `{FLOW_GUID}` โ†’ `{GUID-A}` in `ess-it-servicenow-itsm-system-get-request-details.mcs.yml` and `get-request-details/metadata.yml`. + - `{FLOW_GUID}` โ†’ `{GUID-B}` in `ess-it-servicenow-itsm-system-get-request-item.mcs.yml` and `get-request-item/metadata.yml`. - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in both `workflow.json` files, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. 5. `python scripts/push.py` 6. **Configure OBO** โ€” see below. Do this *before* turning the flows on. @@ -73,17 +93,21 @@ This sample ships **two** flows. Generate a separate GUID for each and repeat th 8. `python scripts/push.py --repair "Get Request Details"` and `--repair "Get Request Item"` to wire topic โ†’ flow. 9. Publish the agent in Copilot Studio. -### Option B โ€” Manual (import the packages) +### Option B โ€” Import the solution + +The solution ships both flows plus its own ServiceNow connection reference +(`esss_servicenow`), so there is exactly one connection to map at import time. -1. In [Power Automate](https://make.powerautomate.com), select **My flows** โ†’ **Import** โ†’ **Import Package (Legacy)**. -2. Upload `workflows/get-request-details/get-request-details.zip`. Repeat the import for `workflows/get-request-item/get-request-item.zip`. -3. For each package, under **Related resources**, select each connection and pick the matching connection in your environment โ€” one **ServiceNow**, one **Microsoft Dataverse**. The display names baked into the package (`SNOW PP2`, `ESS HR`) are from the authoring tenant and are replaced by whatever you select. -4. **Configure OBO** โ€” see below. -5. Turn both flows on and copy each flow ID from its URL. -6. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. -7. Publish. +1. In [Power Apps](https://make.powerapps.com), select your environment, then **Solutions** โ†’ **Import solution**. +2. **Browse** to `solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip` โ†’ **Next**. +3. Under **Connections**, map **ESS Sample ServiceNow Connection** to your ServiceNow connection (or create one inline). The Dataverse binding resolves to the connection reference already present from your ServiceNow extension pack โ€” it is not part of this solution and needs no mapping. +4. Select **Import** and wait for it to complete. +5. **Configure OBO** โ€” see below. +6. In [Power Automate](https://make.powerautomate.com), confirm **ESS IT ServiceNow ITSM Get Request Details** and **ESS IT ServiceNow ITSM Get Request Item** are **On**, and copy each flow ID from its URL. +7. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. Then update the two `dialog:` references in the user-facing topic to the portal's PascalCase schema names โ€” see the schema-name note under [Files](#-files). +8. Publish. -> The packages already declare the ServiceNow connection as `"source": "Invoker"`, so an imported +> The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported > flow is wired for OBO from the start. You still need the connection-side step below. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip new file mode 100644 index 0000000000000000000000000000000000000000..484dad1270e40b368592d78503f8259279cf39a4 GIT binary patch literal 7770 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 QA^my#|IBI##lOD(3u%NmB>(^b literal 0 HcmV?d00001 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-it-servicenow-itsm-system-get-request-details.mcs.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-details.mcs.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-it-servicenow-itsm-system-get-request-details.mcs.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-it-servicenow-itsm-system-get-request-item.mcs.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-hr-servicenow-itsm-system-get-request-item.mcs.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/ess-it-servicenow-itsm-system-get-request-item.mcs.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml index 1354f9dc..1e7d93b0 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/topics/servicenow-itsm-get-request-details.mcs.yml @@ -79,14 +79,14 @@ beginDialog: displayName: Is this a RITM (request item)? actions: # Requires the companion "servicenow-itsm-get-request-item" sample to be installed - # (provides the ess-hr-servicenow-itsm-system-get-request-item system topic + flow). + # (provides the ess-it-servicenow-itsm-system-get-request-item system topic + flow). - kind: BeginDialog id: callSystemGetRequestItem input: binding: SysId: =Topic.RequestIdentifier - dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-item + dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-request-item output: binding: ServiceNowData: Topic.ServiceNowData @@ -236,7 +236,7 @@ beginDialog: binding: SysId: =Topic.RequestIdentifier - dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-request-details + dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-request-details output: binding: ServiceNowData: Topic.ServiceNowData diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/get-request-details.zip deleted file mode 100644 index aacb53b4f183f24b4ac4e21e777d70d5baf8cf24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4112 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 Q*L26v=VOhGEPtN<1!;`(zyJUM diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml index c277df5f..6e00b208 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-details/metadata.yml @@ -4,7 +4,7 @@ # Both connrefs can be found in workspace/agents/{slug}/connectionreferences.mcs.yml jsonFileName: workflow/workflow.json workflowId: "{FLOW_GUID}" -name: ESS HR ServiceNow ITSM Get Request Details +name: ESS IT ServiceNow ITSM Get Request Details type: 1 description: "Retrieves details for a specific ServiceNow service catalog request (sc_request table) by REQ number." subprocess: false diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/workflows/get-request-item/get-request-item.zip deleted file mode 100644 index fc59af723c75651dc1a2f66c4f9ca25c9333d57f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4343 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 **โš ๏ธ Topic schema names depend on how you install.** The user-facing topic reaches the system +> topic by schema name, e.g. +> `dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-user-requests` +> (two occurrences). How that schema name gets assigned differs by install path: +> +> | Install path | Schema name derived from | Result | +> |---|---|---| +> | **Option A** (`push.py`) | the **filename** | `โ€ฆtopic.ess-it-servicenow-itsm-system-get-user-requests` | +> | **Option B** (Copilot Studio portal) | the **display name**, spaces removed | `โ€ฆtopic.ESSITServiceNowITSMSystemGetUserRequests` | +> +> The references in this sample are written for **Option A** and resolve as-is. If you install +> via the portal, update both `dialog:` lines in +> `topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml` to the PascalCase form. A mismatch +> fails at publish with `Dialog with id 'โ€ฆ' not found`, followed by a second, misleading +> `IncorrectTypeAssignment` error (`Assigned: Unspecified, expected: String`) โ€” that one is a +> symptom of the unresolved reference, not a separate problem, and clears with it. +> +> Also note the `msdyn_copilotforemployeeselfserviceit` prefix is the **agent's** schema name. +> These topics target the ESS **IT** agent; retarget the prefix if your agent differs. --- @@ -46,7 +66,7 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list 1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. 2. Copy `topics/` โ†’ `workspace/agents/{slug}/topics/`. -3. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-hr-servicenow-itsm-get-user-requests-{NEW-GUID}/`. +3. Copy `workflow/` โ†’ `workspace/agents/{slug}/workflows/ess-it-servicenow-itsm-get-user-requests-{NEW-GUID}/`. 4. Replace the placeholders: - `{FLOW_GUID}` โ€” in the system topic **and** `workflow/metadata.yml` (use the GUID from step 3). - `{SERVICENOW_CONNREF}` / `{DATAVERSE_CONNREF}` โ€” in `workflow.json`, from `workspace/agents/{slug}/connectionreferences.mcs.yml`. @@ -56,17 +76,21 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list 8. `python scripts/push.py --repair "Get User Requests"` to wire topic โ†’ flow. 9. Publish the agent in Copilot Studio. -### Option B โ€” Manual (import the package) +### Option B โ€” Import the solution -1. In [Power Automate](https://make.powerautomate.com), select **My flows** โ†’ **Import** โ†’ **Import Package (Legacy)**. -2. Upload `workflow/get-user-requests.zip`. -3. Under **Related resources**, select each connection and pick the matching connection in your environment โ€” one **ServiceNow**, one **Microsoft Dataverse**. The display names baked into the package (`SNOW PP2`, `ESS HR`) are from the authoring tenant and are replaced by whatever you select. -4. **Configure OBO** โ€” see below. -5. Turn the flow on and copy its ID from the URL. -6. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. -7. Publish. +The solution ships the flow plus its own ServiceNow connection reference +(`esss_servicenow`), so there is exactly one connection to map at import time. -> The package already declares the ServiceNow connection as `"source": "Invoker"`, so an imported +1. In [Power Apps](https://make.powerapps.com), select your environment, then **Solutions** โ†’ **Import solution**. +2. **Browse** to `solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip` โ†’ **Next**. +3. Under **Connections**, map **ESS Sample ServiceNow Connection** to your ServiceNow connection (or create one inline). The Dataverse binding resolves to the connection reference already present from your ServiceNow extension pack โ€” it is not part of this solution and needs no mapping. +4. Select **Import** and wait for it to complete. +5. **Configure OBO** โ€” see below. +6. In [Power Automate](https://make.powerautomate.com), confirm **ESS IT ServiceNow ITSM Get User Requests** is **On**, and copy the flow ID from its URL. +7. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. Then update both `dialog:` references in the user-facing topic to the portal's PascalCase schema name โ€” see the schema-name note under [Files](#-files). +8. Publish. + +> The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported > flow is wired for OBO from the start. You still need the connection-side step below. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip new file mode 100644 index 0000000000000000000000000000000000000000..f6357207fc3d13b7ef35687dd466b8827ddc3f00 GIT binary patch literal 5302 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 literal 0 HcmV?d00001 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml similarity index 94% rename from copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml index 4723b15b..0d7be27a 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-get-user-requests.mcs.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml @@ -61,7 +61,7 @@ beginDialog: IsActive: =Blank() UserIdentifier: =Global.ESS_UserContext_UPN - dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-user-requests + dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-user-requests output: binding: ServiceNowData: Topic.ServiceNowData @@ -79,7 +79,7 @@ beginDialog: IsActive: =Topic.IsActive UserIdentifier: =Global.ESS_UserContext_UPN - dialog: msdyn_copilotforemployeeselfservicehr.topic.ess-hr-servicenow-itsm-system-get-user-requests + dialog: msdyn_copilotforemployeeselfserviceit.topic.ess-it-servicenow-itsm-system-get-user-requests output: binding: ServiceNowData: Topic.ServiceNowData diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-system-get-user-requests.mcs.yml similarity index 100% rename from copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-hr-servicenow-itsm-system-get-user-requests.mcs.yml rename to copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-system-get-user-requests.mcs.yml diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/get-user-requests.zip deleted file mode 100644 index 4060aeebd7a0bd41519694d99cb5355e804eb378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4203 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 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml index a39e18a0..842baa02 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/metadata.yml @@ -1,6 +1,6 @@ jsonFileName: workflows/workflow/workflow.json workflowId: {FLOW_GUID} -name: ESS HR ServiceNow ITSM Get User Requests +name: ESS IT ServiceNow ITSM Get User Requests type: 1 description: "Retrieves a list of service catalog requests (sc_request) for the current user." subprocess: false From dce9a95daea374d435e30237179a3169e5660fc0 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 17:43:04 -0400 Subject: [PATCH 18/20] ESS ServiceNow ITSM samples: fix OutputFieldMapping encoding, expand Option B setup Fix Get Request Item flow returning 400 Bad Request at Map_output_fields. The item/OutputFieldMapping parameter was wrapped in a stray pair of quotes with the inner quotes unescaped, so msdyn_ServiceNowOutputFieldMapperPlugin could not parse it. Repaired the affected workflow entry in the solution package (the repo workflow.json was already correct). Expand Option B (solution import) in both READMEs into five explicit sub-steps: import and connection mapping, OBO configuration in Copilot Studio before testing, flow ID retrieval with a labeled Power Automate URL, topic creation with a file-to-topic-name table and the literal PascalCase dialog references, and publish. Trim ess-it-servicenow-itsm-get-user-requests modelDescription from 1039 to 935 chars to clear the 1024 limit. Routing rules and the link format string are unchanged. --- .../README.md | 74 +++++++++++++++--- ...erviceNowITSMGetRequestDetails_1_0_0_0.zip | Bin 7770 -> 7609 bytes .../README.md | 66 +++++++++++++--- ...-servicenow-itsm-get-user-requests.mcs.yml | 8 +- 4 files changed, 126 insertions(+), 22 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 8c81d2ba..83f7dd6e 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -98,17 +98,73 @@ This sample ships **two** flows. Generate a separate GUID for each and repeat th The solution ships both flows plus its own ServiceNow connection reference (`esss_servicenow`), so there is exactly one connection to map at import time. -1. In [Power Apps](https://make.powerapps.com), select your environment, then **Solutions** โ†’ **Import solution**. -2. **Browse** to `solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip` โ†’ **Next**. -3. Under **Connections**, map **ESS Sample ServiceNow Connection** to your ServiceNow connection (or create one inline). The Dataverse binding resolves to the connection reference already present from your ServiceNow extension pack โ€” it is not part of this solution and needs no mapping. -4. Select **Import** and wait for it to complete. -5. **Configure OBO** โ€” see below. -6. In [Power Automate](https://make.powerautomate.com), confirm **ESS IT ServiceNow ITSM Get Request Details** and **ESS IT ServiceNow ITSM Get Request Item** are **On**, and copy each flow ID from its URL. -7. In Copilot Studio, create the three topics from `topics/`, replacing `{FLOW_GUID}` in each system topic with its matching flow ID. Then update the two `dialog:` references in the user-facing topic to the portal's PascalCase schema names โ€” see the schema-name note under [Files](#-files). -8. Publish. +#### B1 โ€” Import the solution + +1. Go to [Power Apps](https://make.powerapps.com) and confirm the environment picker (top right) shows your target environment. +2. Select **Solutions** in the left nav, then **Import solution** on the command bar. +3. Select **Browse**, choose `solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip`, then **Next**. +4. On the **Connections** step, map **ESS Sample ServiceNow Connection** to your ServiceNow connection. If you don't have one, select **+ New connection**, create it, then return and select **Refresh**. + - You will **not** be asked to map a Dataverse connection. The flows bind Dataverse through `new_sharedcommondataserviceforapps_41c83`, which is already in your environment courtesy of the ServiceNow extension pack. +5. Select **Import** and wait for the success banner. + +#### B2 โ€” Configure OBO (do this before testing) + +Both flows call ServiceNow **as the signed-in employee**, which requires parameter sharing on the +ServiceNow connection. This is configured in **Copilot Studio**, not Power Automate โ€” follow +[Configure OBO](#-configure-obo-required) below, then return here. + +#### B3 โ€” Collect the two flow IDs + +1. In [Power Automate](https://make.powerautomate.com), select **Solutions**, then open **ESS ServiceNow ITSM - Get Request Details**. +2. Confirm both flows show **Status: On**. If either is **Off**, open it and select **Turn on**. + - `ESS IT ServiceNow ITSM Get Request Details` + - `ESS IT ServiceNow ITSM Get Request Item` +3. Open each flow and read its ID from the browser address bar โ€” it's the GUID immediately after `/flows/`: + + ```text + https://make.powerautomate.com/environments/{env}/solutions/{sol}/flows/5750fc72-738f-412c-b9cb-5094507f2eb2/details + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ flow ID โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + ``` + + Record one ID per flow โ€” they are different values. Keep them straight by flow name: swapping them sends REQ lookups to the RITM flow and vice versa, which fails silently with an empty card rather than an error. + +#### B4 โ€” Create the three topics + +For each file in `topics/`: + +1. In [Copilot Studio](https://copilotstudio.microsoft.com/), open your agent and select **Topics** โ†’ **+ Add a topic** โ†’ **From blank**. +2. Open the **โ‹ฎ** menu and select **Open code editor**. +3. Paste the entire contents of the `.mcs.yml` file, replacing whatever is already there. +4. Set the topic name **exactly** as below. The name matters: the portal derives the topic's schema name from it, and the user-facing topic locates the system topics by schema name. + + | File | Topic name to use | + |------|-------------------| + | `ess-it-servicenow-itsm-system-get-request-details.mcs.yml` | `ESS IT ServiceNow ITSM System Get Request Details` | + | `ess-it-servicenow-itsm-system-get-request-item.mcs.yml` | `ESS IT ServiceNow ITSM System Get Request Item` | + | `servicenow-itsm-get-request-details.mcs.yml` | `ESS ServiceNow ITSM Get Request Details` | + +5. In each **system** topic, replace `{FLOW_GUID}` with that topic's matching flow ID from B3: + - `โ€ฆsystem-get-request-details` โ†’ ID of **Get Request Details** + - `โ€ฆsystem-get-request-item` โ†’ ID of **Get Request Item** +6. In the **user-facing** topic, replace both `dialog:` values with the PascalCase schema names the portal generated: + + ```yaml + dialog: msdyn_copilotforemployeeselfserviceit.topic.ESSITServiceNowITSMSystemGetRequestDetails + dialog: msdyn_copilotforemployeeselfserviceit.topic.ESSITServiceNowITSMSystemGetRequestItem + ``` + + The shipped values are kebab-case (correct for Option A only). Leaving them produces + `Dialog with id 'โ€ฆ' not found` at publish โ€” see the note under [Files](#-files). +7. **Save** each topic. + +#### B5 โ€” Publish + +Select **Publish** in Copilot Studio. If publish reports `Dialog with id โ€ฆ not found`, revisit +step B4.6; if it reports `IncorrectTypeAssignment` alongside it, that's the same fault, not a +second one. > The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported -> flow is wired for OBO from the start. You still need the connection-side step below. +> flow is wired for OBO from the start. You still need the connection-side step in B2. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip index 484dad1270e40b368592d78503f8259279cf39a4..6909763a02cbe87cb0c3f6ddd462f8a5657cffd2 100644 GIT binary patch delta 2475 zcmY+`dpy(oAHebNX5_ZWM6S8t85wh#L?p&^MCMi&<=&J{w#8gJxs^+EO%j?*qRYmK zT*E3%%q5cKPPvvPoz&6ear*r_pFiJ^=lk*g{P}u~syEj^hUS5Y0|3Ael&13dz;(6m zIzj+ok0=0$?M(fM5d?f#5E(}Z!k>%KiwX;U=;#7b`Nikf+j}c_xT+KY2=DL+d?@j! z{0SLvA@?B?zvNgotcJl40HV48fCXsx8GAobJ3Y?KVg7bbW zYQ^(%zFRdNT#|HWDs79<%bPr1qQOt4samL}!4_> ztu19Mde*4$HQ1m;Plv$eDTtEtbja z!#N6lk!#bZxb%YmUDPwM*381?t$6f7_rufVyQ+5ZXS5;_)*AId!~9gF{Kvm9%}usk zx3e_)5FL9+Bkvv0K4y$5Z4&#V+xE1Qf|{U8m&``I%6mS7VlXxdhq-?FJ&yoP?na{Z z8(-Jz)366pT-=$}nje3TxB7jfDoto_F-?PqLjqmnbtP(ft-ryeK7~DEXE*`-kW1KIaQwOBgsb?b{1%J@#!76^Kidpg!)IPoSfY zPDX{dD=pVYtUt4q)SYS*)UXN&du*m~C2%*zL4{eih9!N(rqW}V{hwmLjEKJIny%t( zP6?;$9#iJOURa62h&_T2nwKBAa{jBi$O)&u?oLb5w&zBn*6Mf0=^QZ+6>!njb7;LG zWU1gla0PlI)k-@+JAAQUmWpM~7<57A#orxXt=KdlRKb%C%-7RMh=9~Glcv-lf;eNk zQJ5fjoM)0=D!mw-ot;dF2$Km?iI;3E?@Xeqr5r4kQ@*kedpN(9RJhk(YkVO9druD; zPz*(D<=P16+2JwiG4Ja?PxkbZyH<(b-W^w`3`X*@th^*5&OPp+@*jRRRXJ(I{C0Dl zqLp?Vxi9XfY*?^oa7~byp=w9(dX7DD_jwpdsX#JeAdI5Ic!ycj-bFVxtumeI5t}Gy z_N|RL3v6gGCk~CTaZ;w7K;{$T90%G{TULMbF7E|{WgFdZ7gXB@IYN05!hM$>PfnP4 zS<$w>fJUB^moA*@->)0Uw$~^aQrKs0H%j6y*llrSqL#bpez6ZNRQERV`*-|)ANI?Y zp%_NHqSpcFvVAGU^}c>anSk+t@C;P4OMCD|oNGoOPk5(IfWlG?G$|`|4~TE?{#YY; zGU&>hTS%+Bgz!Ptjt8vUSYnHZn#A8TPA@jqqper6pL{8#!fLL~7iC?blMGc{)rO)` z&cbpCBiSVo_XnrX$c&X^~ePY97qnh(XzBpu`{S)_uTL%?4#?#$bzQtAn#B0XFl5aU*zkpGKINtUyb1XsfUl>czUGlL+k~S3888R#m$1O zFyA;w5_FK=i4Pf67X-9OBA5VdO^#JSf`bQr$8K4*$^RvvKTv(;Jj)d0nJhzs z;80RR`E$077KfL=TLhRHEn6EzJbC$bGn3WlmJ%+@? zk4BwPYW_UuK38F})Fhj(-TsQZUl&`eb@6F&*tQ=edNy|xTDD7R(oWX=o-@bLAn^ZJd3^pGG&^VheX$^Cfq(PHNd5PP|JKbf4gj&BC|Z@A Y_wPU0BQMPp3lgB|$*b{A$o!=L0Qi(*a{vGU delta 2674 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 diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 2a99f244..5e817d50 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -81,17 +81,65 @@ Extends the Microsoft Employee Self-Service (ESS) agent with the ability to list The solution ships the flow plus its own ServiceNow connection reference (`esss_servicenow`), so there is exactly one connection to map at import time. -1. In [Power Apps](https://make.powerapps.com), select your environment, then **Solutions** โ†’ **Import solution**. -2. **Browse** to `solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip` โ†’ **Next**. -3. Under **Connections**, map **ESS Sample ServiceNow Connection** to your ServiceNow connection (or create one inline). The Dataverse binding resolves to the connection reference already present from your ServiceNow extension pack โ€” it is not part of this solution and needs no mapping. -4. Select **Import** and wait for it to complete. -5. **Configure OBO** โ€” see below. -6. In [Power Automate](https://make.powerautomate.com), confirm **ESS IT ServiceNow ITSM Get User Requests** is **On**, and copy the flow ID from its URL. -7. In Copilot Studio, create the two topics from `topics/`, replacing `{FLOW_GUID}` with that ID. Then update both `dialog:` references in the user-facing topic to the portal's PascalCase schema name โ€” see the schema-name note under [Files](#-files). -8. Publish. +#### B1 โ€” Import the solution + +1. Go to [Power Apps](https://make.powerapps.com) and confirm the environment picker (top right) shows your target environment. +2. Select **Solutions** in the left nav, then **Import solution** on the command bar. +3. Select **Browse**, choose `solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip`, then **Next**. +4. On the **Connections** step, map **ESS Sample ServiceNow Connection** to your ServiceNow connection. If you don't have one, select **+ New connection**, create it, then return and select **Refresh**. + - You will **not** be asked to map a Dataverse connection. The flow binds Dataverse through `new_sharedcommondataserviceforapps_41c83`, which is already in your environment courtesy of the ServiceNow extension pack. +5. Select **Import** and wait for the success banner. + +#### B2 โ€” Configure OBO (do this before testing) + +The flow calls ServiceNow **as the signed-in employee**, which requires parameter sharing on the +ServiceNow connection. This is configured in **Copilot Studio**, not Power Automate โ€” follow +[Configure OBO](#-configure-obo-required) below, then return here. + +#### B3 โ€” Collect the flow ID + +1. In [Power Automate](https://make.powerautomate.com), select **Solutions**, then open **ESS ServiceNow ITSM - Get User Requests**. +2. Confirm **ESS IT ServiceNow ITSM Get User Requests** shows **Status: On**. If it's **Off**, open it and select **Turn on**. +3. Open the flow and read its ID from the browser address bar โ€” it's the GUID immediately after `/flows/`: + + ```text + https://make.powerautomate.com/environments/{env}/solutions/{sol}/flows/5f08a17c-ed32-42ea-bbe4-29da0e9e98b1/details + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ flow ID โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + ``` + +#### B4 โ€” Create the two topics + +For each file in `topics/`: + +1. In [Copilot Studio](https://copilotstudio.microsoft.com/), open your agent and select **Topics** โ†’ **+ Add a topic** โ†’ **From blank**. +2. Open the **โ‹ฎ** menu and select **Open code editor**. +3. Paste the entire contents of the `.mcs.yml` file, replacing whatever is already there. +4. Set the topic name **exactly** as below. The name matters: the portal derives the topic's schema name from it, and the user-facing topic locates the system topic by schema name. + + | File | Topic name to use | + |------|-------------------| + | `ess-it-servicenow-itsm-system-get-user-requests.mcs.yml` | `ESS IT ServiceNow ITSM System Get User Requests` | + | `ess-it-servicenow-itsm-get-user-requests.mcs.yml` | `ESS IT ServiceNow ITSM Get User Requests` | + +5. In the **system** topic, replace `{FLOW_GUID}` with the flow ID from B3. +6. In the **user-facing** topic, replace **both** `dialog:` values (they appear twice โ€” once in the all-requests branch, once in the filtered branch) with the PascalCase schema name the portal generated: + + ```yaml + dialog: msdyn_copilotforemployeeselfserviceit.topic.ESSITServiceNowITSMSystemGetUserRequests + ``` + + The shipped values are kebab-case (correct for Option A only). Leaving them produces + `Dialog with id 'โ€ฆ' not found` at publish โ€” see the note under [Files](#-files). +7. **Save** each topic. + +#### B5 โ€” Publish + +Select **Publish** in Copilot Studio. If publish reports `Dialog with id โ€ฆ not found`, revisit +step B4.6; if it reports `IncorrectTypeAssignment` alongside it, that's the same fault, not a +second one. > The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported -> flow is wired for OBO from the start. You still need the connection-side step below. +> flow is wired for OBO from the start. You still need the connection-side step in B2. ### ๐Ÿ” Configure OBO (required) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml index 0d7be27a..14186b3f 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/topics/ess-it-servicenow-itsm-get-user-requests.mcs.yml @@ -18,7 +18,7 @@ modelDescription: |- Use this tool when a user asks about their service requests, catalog requests, or REQ items โ€” NOT for incident tickets (INC). Use ServiceNow-ITSM-Get-User-Tickets for incidents. - Example queries handled by this tool include: + Example queries: * "Show me my service requests" * "What requests do I have open?" * "My pending catalog requests" @@ -27,9 +27,9 @@ modelDescription: |- * "Show my REQ items" Use these instructions to respond: - 1. Use the 'ServiceNowData' variable to provide the user with relevant information about their requests. - 2. If no requests are found, inform the user of the same. - 3. If requests are found, inform the user that the requests are sorted by most recently updated. + 1. Use the 'ServiceNowData' variable to answer the user's question. + 2. If no requests are found, tell the user. + 3. If requests are found, note they are sorted by most recently updated. 4. Display them in a numbered list in the format: `**[{RequestNumber}]({ServiceNowPortalBaseURI}?id=ticket&table=sc_request&sys_id={SysId})**: {ShortDescription} ({State})`. 5. Let the user know you can provide more details on any specific request. From 414145e63e21a496d73a5bf346e0d25b36790c16 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 17:58:39 -0400 Subject: [PATCH 19/20] ESS ServiceNow ITSM samples: resolve portal base URI the OOB way Flows now resolve the ServiceNow portal link base using the same chain as the out-of-the-box ESS ITSM orchestrator: a Retrieve_ServiceNow_ITSM_Portal_Base_URI lookup feeding setProperty on the base config, with precedence env var current value -> env var default value -> msdyn_ServiceNowITSM template config property. Previously the samples read only the template config property, which ships empty, so every 'Open in ServiceNow' link rendered without a host. - Add the lookup action and rewire runAfter in all three workflow.json files - Rebuild both solution zips with the updated workflow definitions - Document the ServiceNowITSMPortalBaseURI environment variable as a prerequisite in both READMEs, including /esc vs /sp portal selection - Ignore .local/ so MSAL token caches cannot be committed --- .gitignore | 3 + .../README.md | 48 +++++ ...erviceNowITSMGetRequestDetails_1_0_0_0.zip | Bin 7609 -> 7982 bytes .../get-request-details/workflow.json | 60 +++++- .../workflows/get-request-item/workflow.json | 176 +++++++++++++++--- .../README.md | 48 +++++ ...SServiceNowITSMGetUserRequests_1_0_0_0.zip | Bin 5302 -> 5363 bytes .../workflow/workflow.json | 60 +++++- 8 files changed, 349 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index e791bd71..964eb335 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .vscode/launch.json node_modules/ + +# Local auth/token caches and per-folder scratch state - never commit +.local/ diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 83f7dd6e..5be0f428 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -63,6 +63,7 @@ Each `workflows/*` folder ships the flow definition for the push-script path: - Microsoft Employee Self-Service agent (deployed) - ServiceNow ITSM extension pack installed in Copilot Studio - An active ServiceNow connection in Power Platform configured for on-behalf-of (invoker) auth โ€” see [Configure OBO](#-configure-obo-required) +- The **`ServiceNowITSMPortalBaseURI`** environment variable populated โ€” see [Set the portal base URI](#-set-the-portal-base-uri-required-for-links) - [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (for automated deployment via `push.py`) --- @@ -74,6 +75,10 @@ Each `workflows/*` folder ships the flow definition for the push-script path: > not as the maker. This is what keeps ServiceNow's own row-level ACLs in force โ€” each user > only sees requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. +> **๐Ÿ”— Also required:** set the `ServiceNowITSMPortalBaseURI` environment variable. +> It ships empty, and the "Open in ServiceNow" links render without a host until you set it. +> See [Set the portal base URI](#-set-the-portal-base-uri-required-for-links) below. + ### Option A โ€” [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (recommended) This sample ships **two** flows. Generate a separate GUID for each and repeat the copy/replace for both. @@ -166,6 +171,49 @@ second one. > The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported > flow is wired for OBO from the start. You still need the connection-side step in B2. +### ๐Ÿ”— Set the portal base URI (required for links) + +Both flows in this sample resolve the "Open in ServiceNow" link base the same way the +out-of-the-box ESS orchestrator does: + +1. the **`ServiceNowITSMPortalBaseURI`** environment variable's **current value**, then +2. that variable's **default value**, then +3. the `ServiceNowPortalBaseURI` property of the `msdyn_ServiceNowITSM` template + configuration record. + +**This environment variable ships empty**, and so does the template configuration +property. If you don't set one of them, the adaptive cards still render correctly, +but every "Open in ServiceNow" link points at `?id=ticket&table=...` with no host +and won't resolve. + +1. Go to [make.powerapps.com](https://make.powerapps.com) โ†’ your environment โ†’ + **Solutions** โ†’ **Default Solution** โ†’ **Environment variables**. +2. Open **ServiceNowITSMPortalBaseURI**. +3. Set the **Current value** to your ServiceNow portal base URL โ€” no trailing + slash, no query string: + + ```text + https://.service-now.com/esc + ``` + + Use `/esc` for **Employee Center** or `/sp` for the classic **Service Portal**, + whichever your employees use. To confirm which is default in your instance, go + to **Service Portal โ†’ Portals** in ServiceNow and check the **URL suffix** of + the portal flagged as default. +4. **Save**. + +The topics append `?id=ticket&table=sc_request&sys_id=...` to this value, so a +finished link looks like: + +```text +https://.service-now.com/esc?id=ticket&table=sc_request&sys_id=127a3aef8356cf1028e6cc65eeaad3d4 +``` + +> This variable is shared with the out-of-the-box ServiceNow ITSM topics โ€” +> setting it fixes their links too. + +--- + ### ๐Ÿ” Configure OBO (required) Both flows must call ServiceNow as the signed-in employee. Enable parameter sharing on the diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/solution/ESSServiceNowITSMGetRequestDetails_1_0_0_0.zip index 6909763a02cbe87cb0c3f6ddd462f8a5657cffd2..46c90a247b3f4dc6ebc12c4bcc86b576bbac041b 100644 GIT binary patch delta 4902 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 Y0uzpta2*^1ejJmI9VG@-9RL6T0NT!H(*OVf delta 4558 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 not as the maker. This keeps ServiceNow's own row-level ACLs in force โ€” each user only sees > requests they're entitled to. See [Configure OBO](#-configure-obo-required) below. +> **๐Ÿ”— Also required:** set the `ServiceNowITSMPortalBaseURI` environment variable. +> It ships empty, and the "Open in ServiceNow" links render without a host until you set it. +> See [Set the portal base URI](#-set-the-portal-base-uri-required-for-links) below. + ### Option A โ€” [ESS Maker Kit](https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/tree/main/solutions/ess-maker-skills) (recommended) 1. Run `/setup` in your ESS Maker Kit workspace to connect to the environment. @@ -141,6 +146,49 @@ second one. > The solution declares the ServiceNow connection as `"runtimeSource": "invoker"`, so an imported > flow is wired for OBO from the start. You still need the connection-side step in B2. +### ๐Ÿ”— Set the portal base URI (required for links) + +The flow in this sample resolve the "Open in ServiceNow" link base the same way the +out-of-the-box ESS orchestrator does: + +1. the **`ServiceNowITSMPortalBaseURI`** environment variable's **current value**, then +2. that variable's **default value**, then +3. the `ServiceNowPortalBaseURI` property of the `msdyn_ServiceNowITSM` template + configuration record. + +**This environment variable ships empty**, and so does the template configuration +property. If you don't set one of them, the adaptive cards still render correctly, +but every "Open in ServiceNow" link points at `?id=ticket&table=...` with no host +and won't resolve. + +1. Go to [make.powerapps.com](https://make.powerapps.com) โ†’ your environment โ†’ + **Solutions** โ†’ **Default Solution** โ†’ **Environment variables**. +2. Open **ServiceNowITSMPortalBaseURI**. +3. Set the **Current value** to your ServiceNow portal base URL โ€” no trailing + slash, no query string: + + ```text + https://.service-now.com/esc + ``` + + Use `/esc` for **Employee Center** or `/sp` for the classic **Service Portal**, + whichever your employees use. To confirm which is default in your instance, go + to **Service Portal โ†’ Portals** in ServiceNow and check the **URL suffix** of + the portal flagged as default. +4. **Save**. + +The topics append `?id=ticket&table=sc_request&sys_id=...` to this value, so a +finished link looks like: + +```text +https://.service-now.com/esc?id=ticket&table=sc_request&sys_id=127a3aef8356cf1028e6cc65eeaad3d4 +``` + +> This variable is shared with the out-of-the-box ServiceNow ITSM topics โ€” +> setting it fixes their links too. + +--- + ### ๐Ÿ” Configure OBO (required) The flow must call ServiceNow as the signed-in employee so ServiceNow enforces per-user access diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/solution/ESSServiceNowITSMGetUserRequests_1_0_0_0.zip index f6357207fc3d13b7ef35687dd466b8827ddc3f00..8fa56f111dc68a62d1eaabd74824481d696848a0 100644 GIT binary patch delta 2831 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 delta 2796 zcmajhc{tSj0>|+mgNAHb$C8A`5+*whl67oJw#hEx$TFC*$B)KxNWvi3o-&p!ag^+Y z?E6l}UY5w3CClwR_c`~ed+u}Z_wV=j^Zow$+7W&$tan=%Lcs!%15ltb17cOx5U$?? z0RUR0ef#O4&&w=V25+YxMRir++2BcV1Q6I3-I4g*|i~v%D<NP&4pd`FE|_@*=}rH-}i!2K~jnGjA2A}dGI}L-c4^#iUV>9?jtgN zrql=&Z|sB^wTC)KXL+h9>P5`3maV7;bwtyXWS!hdyVm{3=^A8hy)X=zgH3~&p|v-0oy z_HIIX24D%h$zds5Tf)?^C21!9H=(MJpW1xYJN=^$awfQd>~zmbAtmWPJ)M_6X%fq# zZc%Hbxzv>Ntve&eXR#q#F9O!08G>4Hu2Zc-hscKY!9Z6u`%>A#@AH-s%k`WpDQsPO z8t&1Mankp-hS{d^_a}u9J&p_3D$K446xiNZ#$Z@^G-KySJFgD0A7EpvJl4+7#7P@- z)n4}O<{}96cnwTG7A2UdO2b9E4C@GXx{YCW-scY-wuf{@=M-u4ZXM-)TF)YLw3~oHGF!%vPO1%gyHsDbo?xUc%~IPsyYiPCHx!Rxb@o z^MP2GZjzJybYHuj_OOzg<3i3o^Z6KbNxtG-T!Au+iQY39mRpOm^t=L9E{71JiVS5F zS`qVcva^_v9dG+Gx}K%mh}@DlD-nMKnaPvB>fN`T5fkfF&i@;gCj3c@JGA<6~Fbdud$hV_rI#REU1R}(%4gJ3kDe_cEBeJf?y({T7)!mB=>>8V1i{q8nR z*MJd^SG^ydc_?6Jfu<)$r8K&$mlg%+gXoS=&%C<7$a|s)Fv_zKPU$0^hBE^%OnChNpPu)QkWQ|Mg)_IIuD2X%(Su>j^!?~>67X;A9 zIB=yh_+*I6Th%NiIWY`n*QXgCtvnOT5%iJ1~u7=5H z@W?>X(R`X>zcK7@;W8a{2SM2EQ*Xi`4H>=QFo(fyVR zZWjp9XFF<~I?u!&8#8Jh*Mo{4Rpb`wQYq9rHktHBG4`7S33c$iMrr+2Cg8s#G` zYiBB%laF6sHL>4W3UW%;jDfFm1i77a)LY+kd!|q+Q@RG~(u6$pt{t@>Rd*Zkx2lnG zlQFj6sg9oN>BQXSur-|p|<+E_Zl)6B!|6iktju1$<_ z!0Q9+75I^#^@W*5U#p5|V|1)(eS18_t?{)n#OR`-zMe%=m?>+JUWs%5Wo+9fs?qtq zO4|UW4Gt2^vrshUj~Sz|5y*}b#I##Tg4S&kd{AmIb0RLZrZ?Y+h1{2Kg6%HJPv-DT zPcK)R<>P^2rG)7(_NeuN3deRSqgAD@^~jU=uh(c^X6&`aMSh6l&^E^2OGza$PE;u_ zWf}=)tFy=h_XUVz^o6ze5F!g{V4HQYBGYQZHIRMoNXh+0Smx&Z-c@Tr4rX3I9{=@l zEWG1&J4Z-8KNn*j&U<*DS5v+;c`*V#Omi^7@2T$`6Tdz?0&NXt^P%wOZCjKRAFr~o zEo;;ZqPpBr#Z^i}%2TCIZr6pcy{NeMZMvBEYd^f<$IHR@7?X;;?J|+?2yB z$!X=v_xhZG2w&1Go3u+^CCdH6?kYoyu}d+Th-$fJxmPpb{wDaV`i28mwsU`gj+_ht zn14{sLJi}Nb8yGm82fuTV6FZ`Y#07UKf)nX0gV{&7b6Kp%$)FlMSu6>ZCx_*pVBYP woXP;e@xzHf`|#HYV)d&6e}2CcN+^M%>3_XIC}iRMfmjTojRj6+$NXpiPYpH##{d8T diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json index 4debfebe..f2be2714 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/workflow/workflow.json @@ -57,7 +57,9 @@ "x-ms-dynamically-added": true } }, - "required": ["text"] + "required": [ + "text" + ] } } } @@ -91,14 +93,18 @@ } }, "runAfter": { - "Initialize_variable_-_ResponseBody": ["Succeeded"] + "Initialize_variable_-_ResponseBody": [ + "Succeeded" + ] } }, "Build_Filter_Query": { "type": "Compose", "inputs": "@if(equals(triggerBody()?['text_1'], 'true'), concat('requested_for.email=', triggerBody()?['text'], '^active=true^ORDERBYDESCsys_updated_on'), if(equals(triggerBody()?['text_1'], 'false'), concat('requested_for.email=', triggerBody()?['text'], '^active=false^ORDERBYDESCsys_updated_on'), concat('requested_for.email=', triggerBody()?['text'], '^ORDERBYDESCsys_updated_on')))", "runAfter": { - "Retrieve_Base_ITSM_Config": ["Succeeded"] + "Retrieve_ServiceNow_ITSM_Portal_Base_URI": [ + "Succeeded" + ] } }, "ServiceNow_Get_Requests": { @@ -120,7 +126,9 @@ } }, "runAfter": { - "Build_Filter_Query": ["Succeeded"] + "Build_Filter_Query": [ + "Succeeded" + ] }, "limit": { "timeout": "PT1M" @@ -163,7 +171,7 @@ "actionName": "msdyn_ServiceNowOutputFieldMapperPlugin", "item/ServiceNowTableData": "@body('Flatten_Request_Records')", "item/OutputFieldMapping": "[{\"FieldName\":\"number\",\"OutputName\":\"RequestNumber\"},{\"FieldName\":\"short_description\",\"OutputName\":\"ShortDescription\"},{\"FieldName\":\"request_state\",\"OutputName\":\"State\"},{\"FieldName\":\"requested_for\",\"OutputName\":\"RequestedFor\"},{\"FieldName\":\"assigned_to\",\"OutputName\":\"AssignedTo\"},{\"FieldName\":\"sys_updated_on\",\"OutputName\":\"LastUpdated\"},{\"FieldName\":\"sys_id\",\"OutputName\":\"SysId\"},{\"FieldName\":\"price\",\"OutputName\":\"Price\"}]", - "item/ServiceNowITSMBaseConfiguration": "@first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value']" + "item/ServiceNowITSMBaseConfiguration": "@setProperty(json(coalesce(first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value'], '{}')), 'ServiceNowPortalBaseURI', if(not(empty(coalesce(first(first(outputs('Retrieve_ServiceNow_ITSM_Portal_Base_URI')?['body/value'])?['environmentvariabledefinition_environmentvariablevalue'])?['value'], ''))), first(first(outputs('Retrieve_ServiceNow_ITSM_Portal_Base_URI')?['body/value'])?['environmentvariabledefinition_environmentvariablevalue'])?['value'], if(not(empty(coalesce(first(outputs('Retrieve_ServiceNow_ITSM_Portal_Base_URI')?['body/value'])?['defaultvalue'], ''))), first(outputs('Retrieve_ServiceNow_ITSM_Portal_Base_URI')?['body/value'])?['defaultvalue'], json(coalesce(first(outputs('Retrieve_Base_ITSM_Config')?['body/value'])?['msdyn_value'], '{}'))?['ServiceNowPortalBaseURI'])))" }, "host": { "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", @@ -172,7 +180,9 @@ } }, "runAfter": { - "Flatten_Request_Records": ["Succeeded"] + "Flatten_Request_Records": [ + "Succeeded" + ] } }, "Set_success_response": { @@ -189,7 +199,9 @@ } }, "runAfter": { - "Map_output_fields": ["Succeeded"] + "Map_output_fields": [ + "Succeeded" + ] } } }, @@ -213,7 +225,12 @@ } }, "runAfter": { - "ServiceNow_Get_Requests": ["Succeeded", "Failed", "TimedOut", "Skipped"] + "ServiceNow_Get_Requests": [ + "Succeeded", + "Failed", + "TimedOut", + "Skipped" + ] } }, "Respond_to_Copilot": { @@ -273,11 +290,34 @@ } }, "runAfter": { - "Condition_-_ServiceNow_call_succeeded": ["Succeeded"] + "Condition_-_ServiceNow_call_succeeded": [ + "Succeeded" + ] + } + }, + "Retrieve_ServiceNow_ITSM_Portal_Base_URI": { + "type": "OpenApiConnection", + "inputs": { + "parameters": { + "entityName": "environmentvariabledefinitions", + "$select": "schemaname,defaultvalue", + "$filter": "schemaname eq 'ServiceNowITSMPortalBaseURI'", + "$expand": "environmentvariabledefinition_environmentvariablevalue($select=value)", + "$top": 1 + }, + "host": { + "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps", + "operationId": "ListRecords", + "connectionName": "shared_commondataserviceforapps" + } + }, + "runAfter": { + "Retrieve_Base_ITSM_Config": [ + "Succeeded" + ] } } } } } } - From 12119e13f0c6ee77a4fec39ba8ebe61402ab4ba9 Mon Sep 17 00:00:00 2001 From: Dean Cron Date: Mon, 3 Aug 2026 18:05:52 -0400 Subject: [PATCH 20/20] ESS ServiceNow ITSM samples: record latest publish date in author tables --- .../ESS/servicenow-itsm-get-request-details/README.md | 6 +++--- .../ESS/servicenow-itsm-get-user-requests/README.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md index 5be0f428..482a962d 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-request-details/README.md @@ -256,6 +256,6 @@ running as the maker and every user will see the maker's data โ€” reconfigure be ## Author -| Author | Original Publish Date | -| --- | --- | -| Dean Cron | 2026-07-30 | +| Author | Original Publish Date | Latest Publish Date | +| --- | --- | --- | +| Dean Cron | 2026-07-30 | 2026-08-03 | diff --git a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md index 576bad4e..2175195a 100644 --- a/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md +++ b/copilot-agent-samples/ESS/servicenow-itsm-get-user-requests/README.md @@ -234,6 +234,6 @@ rather than the sole isolation boundary. Note that the 15-minute cache is per-us ## Author -| Author | Original Publish Date | -| --- | --- | -| Dean Cron | 2026-07-30 | +| Author | Original Publish Date | Latest Publish Date | +| --- | --- | --- | +| Dean Cron | 2026-07-30 | 2026-08-03 |