From 4e613972cd56273ce7e0df1a4ea68ed0970791df Mon Sep 17 00:00:00 2001 From: Garima Garg <211866541+ggarima01@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:06:00 -0700 Subject: [PATCH] Update Standard Logic App templates and scripts (logic-app-changes) Update Standard Logic App workflows, orchestrator parameters, and onboarding agent documentation from the validated logic-app-changes export. Bump the Standard Logic App VERSION from 9.0 to 10.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../StandardLogicApp/GroupTests_Workflow.json | 6 +- .../Initialization_Workflow.json | 57 +- .../Orchestrator_Parameters.json | 2 +- .../StandardLogicApp/UserTests_Workflow.json | 11144 ++++++++-------- .../StandardLogicApp/VERSION | 2 +- .../StandardLogicApp/scim-onboarding.agent.md | 122 +- 6 files changed, 5654 insertions(+), 5679 deletions(-) diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json index e3ac86fc..0c45b728 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json @@ -3906,7 +3906,7 @@ "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT50M" + "timeout": "PT20M" }, "actions": { "RestoreGroupTest_SCIM_Check_Created": { @@ -4138,7 +4138,7 @@ "expression": "@equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT50M" + "timeout": "PT20M" }, "actions": { "RestoreGroupTest_SCIM_Check_Gone": { @@ -4380,7 +4380,7 @@ "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT50M" + "timeout": "PT20M" }, "actions": { "RestoreGroupTest_SCIM_Check_Restored": { diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json index 5735f313..1dfc1773 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json @@ -68,6 +68,11 @@ "name": "scimDirectUserCreateBody", "type": "Object", "value": {} + }, + { + "name": "additionalFunctionSources", + "type": "Array", + "value": [] } ] } @@ -216,7 +221,10 @@ "memberUser1Mail": "@{concat('lamem1', substring(guid(),0,8), '@', parameters('testUserDomain'))}", "memberUser2Mail": "@{concat('lamem2', substring(guid(),0,8), '@', parameters('testUserDomain'))}", "scimDirectUser": "@{concat('lascuser', substring(guid(),0,8), '@', parameters('testUserDomain'))}", - "scimDirectUserNickname": "@{concat('lascuser', substring(guid(),0,8))}" + "scimDirectUserNickname": "@{concat('lascuser', substring(guid(),0,8))}", + "podUser": "@{concat('lapod', substring(guid(),0,8), '@', parameters('testUserDomain'))}", + "podUserMail": "@{concat('lapod', substring(guid(),0,8), '@', parameters('testUserDomain'))}", + "podUserNickname": "@{concat('lapod', substring(guid(),0,8))}" } }, "Generate_Test_GroupNames": { @@ -626,6 +634,40 @@ "else": { "actions": {} } + }, + "Select_Transformed_Additional_Sources": { + "runAfter": { + "Condition_Source_Not_Exists": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Select", + "inputs": { + "from": "@if(and(equals(coalesce(items('ForEach_AttributeMappings')?['source']?['type'], ''), 'Function'), greater(length(body('Select_Source_Attribute_Names')), 1)), skip(body('Select_Source_Attribute_Names'), 1), json('[]'))", + "select": "@if(contains(parameters('attributeTransforms'), item()), parameters('attributeTransforms')[item()], item())" + } + }, + "Compose_Union_AdditionalFunctionSources": { + "runAfter": { + "Select_Transformed_Additional_Sources": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@union(variables('additionalFunctionSources'), body('Select_Transformed_Additional_Sources'))" + }, + "Set_AdditionalFunctionSources": { + "runAfter": { + "Compose_Union_AdditionalFunctionSources": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "additionalFunctionSources", + "value": "@outputs('Compose_Union_AdditionalFunctionSources')" + } } }, "else": { @@ -681,8 +723,17 @@ "select": "@item()?['source']" } }, + "Compose_All_Source_Names": { + "runAfter": { + "Select_Graph_Property_Names": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@union(body('Select_Graph_Property_Names'), variables('additionalFunctionSources'))" + }, "For_Each_Build_UserBody": { - "foreach": "@body('Select_Graph_Property_Names')", + "foreach": "@outputs('Compose_All_Source_Names')", "actions": { "Filter_Lookup_For_Source": { "runAfter": {}, @@ -778,7 +829,7 @@ } }, "runAfter": { - "Select_Graph_Property_Names": [ + "Compose_All_Source_Names": [ "Succeeded" ], "Select_ObjectAddOnly_SourceNames": [ diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json index 3871c1bc..b3c4037a 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json @@ -39,7 +39,7 @@ "description": "Version of the Logic App for tracking and deployment purposes" }, "type": "String", - "value": "9.0" + "value": "10.0" }, "attributeTransforms": { "metadata": { diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json index 3c17d955..64df0424 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json @@ -1,5810 +1,5636 @@ { - "definition": { - "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", - "contentVersion": "1.0.0.0", - "triggers": { - "manual": { - "type": "Request", - "kind": "Http", - "inputs": { - "schema": { - "type": "object", - "properties": { - "EnabledTests": { - "type": "string" - }, - "IsSoftDeleted": { - "type": "boolean" - }, - "initializationData": { - "type": "object", - "properties": { - "syncJobId": { - "type": "string" - }, - "testUserNames": { - "type": "object" - }, - "testGroupNames": { - "type": "object" - }, - "appRoles": { - "type": "object" - }, - "userAppRole": { - "type": "array" - }, - "schema": { - "type": "object" - }, - "sourceToTargetLookup": { - "type": "array" - }, - "userBody": { - "type": "object" - }, - "updateUserBody": { - "type": "object" - } + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "triggers": { + "manual": { + "type": "Request", + "kind": "Http", + "inputs": { + "schema": { + "type": "object", + "properties": { + "EnabledTests": { + "type": "string" + }, + "IsSoftDeleted": { + "type": "boolean" + }, + "initializationData": { + "type": "object", + "properties": { + "syncJobId": { + "type": "string" + }, + "testUserNames": { + "type": "object" + }, + "testGroupNames": { + "type": "object" + }, + "appRoles": { + "type": "object" + }, + "userAppRole": { + "type": "array" + }, + "schema": { + "type": "object" + }, + "sourceToTargetLookup": { + "type": "array" + }, + "userBody": { + "type": "object" + }, + "updateUserBody": { + "type": "object" + } + } + } + } + } } - } } - } - } - } - }, - "actions": { - "Initialize_MatchedAttributesResult": { - "runAfter": { - "Initialize_AttributeMatchExcludeList": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "matchedAttributesResult", - "type": "Array", - "value": [] - } - ] - } - }, - "Response_Immediate": { - "runAfter": { - "Initialize_MatchedAttributesResult": [ - "Succeeded" - ] }, - "type": "Response", - "kind": "Http", - "inputs": { - "statusCode": 202, - "body": { - "workflowRunId": "@workflow().run.name", - "workflowName": "@workflow().name", - "status": "Accepted", - "message": "UserTests workflow started. Tests are running asynchronously." - } - } - }, - "Initialize_AttributeMatchExcludeList": { - "runAfter": {}, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "attributeMatchExcludeList", - "type": "Array", - "value": "@triggerBody()?['initializationData']?['updateTestExcludeList']" - } - ] - } - }, - "UserTests_Scope": { "actions": { - "Create_User_Test": { - "actions": { - "Create_User_Test_Actions": { - "actions": { - "Create_User_Initial_Verify_User_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Create_User_Graph": { - "runAfter": { - "Create_User_Initial_Verify_User_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{triggerBody()?['initializationData']?['testUserNames']?['createUser']}", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['createUserNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['createUser']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@if(greater(length(coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json('[]'))), 0), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "otherMails": "@if(greater(length(coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json('[]'))), 0), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json('[]'))", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['createUserMail']}", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Assign_User_To_App": { - "runAfter": { - "Create_User_Graph": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Create_User_Graph')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Create_User_Graph')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DoUntil_Poll_Create_User": { - "actions": { - "SCIM_Query_Created_User": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Create_User_15s": { - "runAfter": { - "SCIM_Query_Created_User": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Assign_User_To_App": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DoUntil_Poll_Provisioning_Logs": { - "actions": { - "Verify_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delay_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Poll_Create_User": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_Failed_Action_Details": { - "actions": { - "Get_Actions_Results": { - "type": "Compose", - "inputs": "@result('Create_User_Test_Actions')" - }, - "Filter_Failed_Actions": { - "runAfter": { - "Get_Actions_Results": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Actions_Results')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateUserFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_Actions')), 0), first(body('Filter_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Failed_Action_Response": { - "runAfter": { - "Set_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateUserFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_Actions')), 0), coalesce(first(body('Filter_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_Create_User_Test_Status_Failed": { - "runAfter": { - "Set_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Create_User_Test_Actions": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_Create_User_Test_Status_Success": { + "Initialize_MatchedAttributesResult": { "runAfter": { - "Create_User_Test_Actions": [ - "Succeeded" - ] + "Initialize_AttributeMatchExcludeList": [ + "Succeeded" + ] }, - "type": "SetVariable", + "type": "InitializeVariable", "inputs": { - "name": "CreateUserScopeExecutionStatus", - "value": "Succeeded" + "variables": [ + { + "name": "matchedAttributesResult", + "type": "Array", + "value": [] + } + ] } - }, - "Create_User_Test_Analyze_Provisioning_Results": { + }, + "Response_Immediate": { "runAfter": { - "Create_User_Test_Actions": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Failed_Action_Details": [ - "Succeeded", - "Skipped" - ], - "Set_Create_User_Test_Status_Success": [ - "Succeeded", - "Skipped" - ] + "Initialize_MatchedAttributesResult": [ + "Succeeded" + ] }, - "type": "Compose", + "type": "Response", + "kind": "Http", "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Verify_Provisioning_Logs: ', coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Verify_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('CreateUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('CreateUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0), false), 'success', 'SCIM_Query_Created_User: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('CreateUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('SCIM_Query_Created_User')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('CreateUserFailedActionName')))", - "overallResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "Set_CreateUser_Output_Variables": { - "actions": { - "Set_CreateUserTestOutputs": { - "type": "SetVariable", - "inputs": { - "name": "CreateUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Create_User_Test_Analyze_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } + "statusCode": 202, + "body": { + "workflowRunId": "@workflow().run.name", + "workflowName": "@workflow().name", + "status": "Accepted", + "message": "UserTests workflow started. Tests are running asynchronously." } - } - }, - "runAfter": { - "Create_User_Test_Analyze_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "Delete_Created_User": { - "runAfter": { - "Set_CreateUser_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Create_User_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } } - } - }, - "else": { - "actions": {} }, - "expression": { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Create_User_Test" - ] + "Initialize_AttributeMatchExcludeList": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "attributeMatchExcludeList", + "type": "Array", + "value": "@triggerBody()?['initializationData']?['updateTestExcludeList']" + } + ] } - ] }, - "type": "If" - }, - "Update_User_Test": { - "actions": { - "Update_User_Test_-_Actions": { + "UserTests_Scope": { "actions": { - "Update_User_Verify_Creation_And_Provisioning": { - "actions": { - "Update_User_-_Initial_Verify_User_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_User_-_Create_User_Graph": { - "runAfter": { - "Update_User_-_Initial_Verify_User_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": "@triggerBody()?['initializationData']?['userBody']", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_User_-_Assign_User_To_App": { - "runAfter": { - "Update_User_-_Create_User_Graph": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Update_User_-_Create_User_Graph')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_DoUntil_Wait_For_User_Creation": { - "actions": { - "Update_User_Verify_User_Created": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}", - "Content-Type": "@{parameters('scimContentType')}" - } - } - }, - "Update_Delay_Creation_15s": { - "runAfter": { - "Update_User_Verify_User_Created": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_User_-_Assign_User_To_App": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "Update_DoUntil_Poll_Create_Provisioning_Logs": { + "Create_User_Test": { "actions": { - "Update_Verify_Create_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Update_User_-_Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Delay_Create_Provisioning_Logs_15s": { - "runAfter": { - "Update_Verify_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] + "Create_User_Test_Actions": { + "actions": { + "Create_User_Initial_Verify_User_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Compose_CreateUser_Body": { + "runAfter": { + "Create_User_Initial_Verify_User_Exists": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['createUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['createUserMail']), 'displayName', triggerBody()?['initializationData']?['testUserNames']?['createUser']), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['createUserNickname'])" + }, + "Create_User_Graph": { + "runAfter": { + "Compose_CreateUser_Body": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_CreateUser_Body')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Assign_User_To_App": { + "runAfter": { + "Create_User_Graph": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Create_User_Graph')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Create_User_Graph')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DoUntil_Poll_Create_User": { + "actions": { + "SCIM_Query_Created_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Create_User_15s": { + "runAfter": { + "SCIM_Query_Created_User": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Assign_User_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Provisioning_Logs": { + "actions": { + "Verify_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delay_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Poll_Create_User": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_DoUntil_Wait_For_User_Creation": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Update_User_Analyze_Create_Provisioning_Results": { - "runAfter": { - "Update_User_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Update_CreatePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_UpdateUser_CreatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Verify_Create_Provisioning_Logs: ', coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Verify_Create_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0), false), 'success', 'Update_User_Verify_User_Created: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Update_User_Verify_User_Created')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('UpdateUserFailedActionName')))", - "overallResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "Update_User_Check_Create_Phase_Status": { - "actions": { - "Update_User_Verify_Update_And_Provisioning": { - "actions": { - "Update_User_Query_User_By_Id": { - "runAfter": {}, - "type": "Http", - "inputs": { - "uri": "@concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_User_Update_User_Attributes": { - "runAfter": { - "Update_User_Query_User_By_Id": [ - "Succeeded", - "Failed" - ] + "Capture_Failed_Action_Details": { + "actions": { + "Get_Actions_Results": { + "type": "Compose", + "inputs": "@result('Create_User_Test_Actions')" + }, + "Filter_Failed_Actions": { + "runAfter": { + "Get_Actions_Results": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Actions_Results')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateUserFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_Actions')), 0), first(body('Filter_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Failed_Action_Response": { + "runAfter": { + "Set_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateUserFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_Actions')), 0), coalesce(first(body('Filter_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_Create_User_Test_Status_Failed": { + "runAfter": { + "Set_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Create_User_Test_Actions": [ + "Failed" + ] + }, + "type": "Scope" }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}", - "method": "PATCH", - "headers": { - "Content-Type": "application/json" - }, - "body": "@triggerBody()?['initializationData']?['updateUserBody']", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_DoUntil_Wait_For_Attribute_Updates": { - "actions": { - "Update_User_-_Query_User_By_Id_After_Attributes_Update": { - "type": "Http", + "Set_Create_User_Test_Status_Success": { + "runAfter": { + "Create_User_Test_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", "inputs": { - "uri": "@concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('Update_User_Query_User_By_Id')?['Resources'])?['id'], ''))", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } + "name": "CreateUserScopeExecutionStatus", + "value": "Succeeded" } - }, - "Update_Delay_Final_15s": { + }, + "Create_User_Test_Analyze_Provisioning_Results": { "runAfter": { - "Update_User_-_Query_User_By_Id_After_Attributes_Update": [ - "Succeeded", - "Failed" - ] + "Create_User_Test_Actions": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Failed_Action_Details": [ + "Succeeded", + "Skipped" + ], + "Set_Create_User_Test_Status_Success": [ + "Succeeded", + "Skipped" + ] }, - "type": "Wait", + "type": "Compose", "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + "provisioningLogsCount": "@length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Verify_Provisioning_Logs: ', coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Verify_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('CreateUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('CreateUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0), false), 'success', 'SCIM_Query_Created_User: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('CreateUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('SCIM_Query_Created_User')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('CreateUserFailedActionName')))", + "overallResult": "@if(equals(variables('CreateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" } - } - }, - "runAfter": { - "Update_User_Update_User_Attributes": [ - "Succeeded" - ] }, - "expression": "@and(not(empty(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'])), if(not(empty(coalesce(triggerBody()?['initializationData']?['updateUserBody']?['userPrincipalName'], ''))), equals(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['userName'], ''), coalesce(triggerBody()?['initializationData']?['updateUserBody']?['userPrincipalName'], '')), if(not(empty(coalesce(triggerBody()?['initializationData']?['updateUserBody']?['displayName'], ''))), equals(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['displayName'], ''), coalesce(triggerBody()?['initializationData']?['updateUserBody']?['displayName'], '')), false)))", - "limit": { - "count": 200, - "timeout": "PT20M" + "Set_CreateUser_Output_Variables": { + "actions": { + "Set_CreateUserTestOutputs": { + "type": "SetVariable", + "inputs": { + "name": "CreateUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Create_User_Test_Analyze_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Create_User_Test_Analyze_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Create_User_Test_Analyze_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" }, - "type": "Until" - }, - "ForEach_SourceToTargetLookup": { - "foreach": "@triggerBody()?['initializationData']?['sourceToTargetLookup']", - "actions": { - "Query_Filter_Array_By_Type": { - "runAfter": {}, - "type": "Query", - "inputs": { - "from": "@if(contains(items('ForEach_SourceToTargetLookup')?['target'], '[type eq'), coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[first(split(items('ForEach_SourceToTargetLookup')?['target'], '['))], json('[]')), json('[]'))", - "where": "@equals(toLower(item()?['type']), toLower(replace(replace(first(split(last(split(items('ForEach_SourceToTargetLookup')?['target'], 'type eq ')), ']')), '\"', ''), '''', '')))" - } - }, - "Condition_Not_In_ExcludeList": { + "Delete_Created_User": { "runAfter": { - "Query_Filter_Array_By_Type": [ - "Succeeded", - "Failed" - ] + "Set_CreateUser_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] }, - "type": "If", - "expression": { - "and": [ - { - "not": { - "contains": [ - "@variables('attributeMatchExcludeList')", - "@items('ForEach_SourceToTargetLookup')?['source']" - ] - } + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Create_User_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" } - ] + } + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Create_User_Test" + ] + } + ] + }, + "type": "If" + }, + "Update_User_Test": { + "actions": { + "Update_User_Test_-_Actions": { "actions": { - "Filter_Join_Sources": { - "runAfter": {}, - "type": "Query", - "inputs": { - "from": "@coalesce(items('ForEach_SourceToTargetLookup')?['functionParameters'], json('[]'))", - "where": "@and(equals(item()?['key'], 'source'), equals(item()?['value']?['type'], 'Attribute'))" - } - }, - "Select_Join_Values": { - "runAfter": { - "Filter_Join_Sources": [ - "Succeeded" - ] + "Update_User_Verify_Creation_And_Provisioning": { + "actions": { + "Update_User_-_Initial_Verify_User_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_User_-_Create_User_Graph": { + "runAfter": { + "Update_User_-_Initial_Verify_User_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@triggerBody()?['initializationData']?['userBody']", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_User_-_Assign_User_To_App": { + "runAfter": { + "Update_User_-_Create_User_Graph": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Update_User_-_Create_User_Graph')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_DoUntil_Wait_For_User_Creation": { + "actions": { + "Update_User_Verify_User_Created": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}", + "Content-Type": "@{parameters('scimContentType')}" + } + } + }, + "Update_Delay_Creation_15s": { + "runAfter": { + "Update_User_Verify_User_Created": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_User_-_Assign_User_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Update_DoUntil_Poll_Create_Provisioning_Logs": { + "actions": { + "Update_Verify_Create_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Update_User_-_Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_Delay_Create_Provisioning_Logs_15s": { + "runAfter": { + "Update_Verify_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_DoUntil_Wait_For_User_Creation": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" }, - "type": "Select", - "inputs": { - "from": "@body('Filter_Join_Sources')", - "select": "@coalesce(triggerBody()?['initializationData']?['updateUserBody']?[item()?['value']?['name']], '')" - } - }, - "Filter_Join_Separator": { - "runAfter": { - "Select_Join_Values": [ - "Succeeded" - ] + "Update_User_Analyze_Create_Provisioning_Results": { + "runAfter": { + "Update_User_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Update_CreatePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_UpdateUser_CreatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Verify_Create_Provisioning_Logs: ', coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Verify_Create_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0), false), 'success', 'Update_User_Verify_User_Created: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Update_User_Verify_User_Created')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('UpdateUserFailedActionName')))", + "overallResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" + } }, - "type": "Query", - "inputs": { - "from": "@coalesce(items('ForEach_SourceToTargetLookup')?['functionParameters'], json('[]'))", - "where": "@equals(item()?['key'], 'separator')" - } - }, - "Compose_Expected_Value": { - "runAfter": { - "Filter_Join_Separator": [ - "Succeeded" - ] + "Update_User_Check_Create_Phase_Status": { + "actions": { + "Update_User_Verify_Update_And_Provisioning": { + "actions": { + "Update_User_Query_User_By_Id": { + "runAfter": {}, + "type": "Http", + "inputs": { + "uri": "@concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['updateUser'], ''), '%22')", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_User_Update_User_Attributes": { + "runAfter": { + "Update_User_Query_User_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}", + "method": "PATCH", + "headers": { + "Content-Type": "application/json" + }, + "body": "@triggerBody()?['initializationData']?['updateUserBody']", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_DoUntil_Wait_For_Attribute_Updates": { + "actions": { + "Update_User_-_Query_User_By_Id_After_Attributes_Update": { + "type": "Http", + "inputs": { + "uri": "@concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('Update_User_Query_User_By_Id')?['Resources'])?['id'], ''))", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_Delay_Final_15s": { + "runAfter": { + "Update_User_-_Query_User_By_Id_After_Attributes_Update": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_User_Update_User_Attributes": [ + "Succeeded" + ] + }, + "expression": "@and(not(empty(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'])), if(not(empty(coalesce(triggerBody()?['initializationData']?['updateUserBody']?['userPrincipalName'], ''))), equals(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['userName'], ''), coalesce(triggerBody()?['initializationData']?['updateUserBody']?['userPrincipalName'], '')), if(not(empty(coalesce(triggerBody()?['initializationData']?['updateUserBody']?['displayName'], ''))), equals(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['displayName'], ''), coalesce(triggerBody()?['initializationData']?['updateUserBody']?['displayName'], '')), false)))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "ForEach_SourceToTargetLookup": { + "foreach": "@triggerBody()?['initializationData']?['sourceToTargetLookup']", + "actions": { + "Query_Filter_Array_By_Type": { + "runAfter": {}, + "type": "Query", + "inputs": { + "from": "@if(contains(items('ForEach_SourceToTargetLookup')?['target'], '[type eq'), coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[first(split(items('ForEach_SourceToTargetLookup')?['target'], '['))], json('[]')), json('[]'))", + "where": "@equals(toLower(item()?['type']), toLower(replace(replace(first(split(last(split(items('ForEach_SourceToTargetLookup')?['target'], 'type eq ')), ']')), '\"', ''), '''', '')))" + } + }, + "Condition_Not_In_ExcludeList": { + "runAfter": { + "Query_Filter_Array_By_Type": [ + "Succeeded", + "Failed" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "not": { + "contains": [ + "@variables('attributeMatchExcludeList')", + "@items('ForEach_SourceToTargetLookup')?['source']" + ] + } + } + ] + }, + "actions": { + "Filter_Join_Sources": { + "runAfter": {}, + "type": "Query", + "inputs": { + "from": "@coalesce(items('ForEach_SourceToTargetLookup')?['functionParameters'], json('[]'))", + "where": "@and(equals(item()?['key'], 'source'), equals(item()?['value']?['type'], 'Attribute'))" + } + }, + "Select_Join_Values": { + "runAfter": { + "Filter_Join_Sources": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Join_Sources')", + "select": "@coalesce(triggerBody()?['initializationData']?['updateUserBody']?[item()?['value']?['name']], '')" + } + }, + "Filter_Join_Separator": { + "runAfter": { + "Select_Join_Values": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@coalesce(items('ForEach_SourceToTargetLookup')?['functionParameters'], json('[]'))", + "where": "@equals(item()?['key'], 'separator')" + } + }, + "Compose_Expected_Value": { + "runAfter": { + "Filter_Join_Separator": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'Join'), join(body('Select_Join_Values'), string(coalesce(first(body('Filter_Join_Separator'))?['value']?['name'], first(body('Filter_Join_Separator'))?['value']?['expression'], ' '))), if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'ToLower'), toLower(string(if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))), if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'ToUpper'), toUpper(string(if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))), if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))))" + }, + "Append_Matched_Attribute_Result": { + "runAfter": { + "Compose_Expected_Value": [ + "Succeeded" + ] + }, + "type": "AppendToArrayVariable", + "inputs": { + "name": "matchedAttributesResult", + "value": { + "sourceAttribute": "@items('ForEach_SourceToTargetLookup')?['source']", + "targetAttribute": "@items('ForEach_SourceToTargetLookup')?['target']", + "expectedValue": "@outputs('Compose_Expected_Value')", + "actualValue": "@if(contains(items('ForEach_SourceToTargetLookup')?['target'], '[type eq'), if(contains(items('ForEach_SourceToTargetLookup')?['target'], '].'), first(body('Query_Filter_Array_By_Type'))?[last(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))], first(body('Query_Filter_Array_By_Type'))?['value']), if(and(contains(items('ForEach_SourceToTargetLookup')?['target'], '.'), not(contains(items('ForEach_SourceToTargetLookup')?['target'], ':'))), body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[first(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))], if(contains(items('ForEach_SourceToTargetLookup')?['target'], ':'), coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[substring(items('ForEach_SourceToTargetLookup')?['target'], 0, lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'))]?[substring(items('ForEach_SourceToTargetLookup')?['target'], add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1), sub(length(items('ForEach_SourceToTargetLookup')?['target']), add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1)))], body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[substring(items('ForEach_SourceToTargetLookup')?['target'], add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1), sub(length(items('ForEach_SourceToTargetLookup')?['target']), add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1)))]), body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[items('ForEach_SourceToTargetLookup')?['target']])))" + } + } + } + }, + "else": { + "actions": {} + } + } + }, + "runAfter": { + "Update_DoUntil_Wait_For_Attribute_Updates": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "type": "Foreach", + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + } + }, + "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": { + "runAfter": { + "ForEach_SourceToTargetLookup": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@variables('matchedAttributesResult')" + }, + "Update_User_-_Filter_Mismatched_Attributes": { + "runAfter": { + "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@coalesce(outputs('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]'))", + "where": "@not(equals(toLower(string(coalesce(item()?['expectedValue'], ''))), toLower(string(coalesce(item()?['actualValue'], '')))))" + } + }, + "Update_User_-_Attribute_Match_Summary": { + "runAfter": { + "Update_User_-_Filter_Mismatched_Attributes": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": { + "totalAttributesVerified": "@length(coalesce(body('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]')))", + "allAttributesMatch": "@equals(length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))), 0)", + "mismatchCount": "@length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]')))", + "mismatchedAttributes": "@coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))", + "matchedCount": "@sub(length(coalesce(body('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]'))), length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))))" + } + }, + "Update_DoUntil_Poll_Update_Provisioning_Logs": { + "actions": { + "Update_Verify_Update_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Update_User_-_Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27&$top=1&$orderby=activityDateTime%20desc')", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_Delay_Update_Provisioning_Logs_15s": { + "runAfter": { + "Update_Verify_Update_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": [ + "Succeeded", + "Failed" + ] + }, + "expression": "@greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "Capture_Update_UpdatePhase_Failed_Actions": { + "actions": { + "Get_Failed_UpdatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Update_User_Verify_Update_And_Provisioning')" + }, + "Filter_Failed_UpdatePhase_Actions": { + "runAfter": { + "Get_Failed_UpdatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_UpdatePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_UpdateUser_UpdatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_UpdatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_UpdatePhase_Actions')), 0), first(body('Filter_Failed_UpdatePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_UpdateUser_UpdatePhase_Failed_Action_Response": { + "runAfter": { + "Set_UpdateUser_UpdatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_UpdatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_UpdatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_UpdateUser_UpdatePhase_Status_Failed": { + "runAfter": { + "Set_UpdateUser_UpdatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Update_User_Verify_Update_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_UpdateUser_UpdatePhase_Status_Success": { + "runAfter": { + "Update_User_Verify_Update_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Set_UpdateUser_UpdatePhase_Output_Variables": { + "actions": { + "Set_UpdateUserTest_UpdatePhase_Outputs": { + "type": "SetVariable", + "inputs": { + "name": "UpdateUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Update Phase] ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Update Phase] Provisioning: ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Update_Analyze_Update_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Update_Analyze_Update_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "Update_Analyze_Update_Provisioning_Results": { + "runAfter": { + "Update_User_Verify_Update_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Update_UpdatePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_UpdateUser_UpdatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), if(equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true), 'success', concat('Update_User_-_Attribute_Match_Summary: Attribute mismatch: ', string(outputs('Update_User_-_Attribute_Match_Summary')?['mismatchCount']), ' attributes failed')), concat('Update_Verify_Update_Provisioning_Logs: ', coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Verify_Update_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], if(not(equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true)), outputs('Update_User_-_Attribute_Match_Summary')?['mismatchedAttributes'], null)), variables('UpdateUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], ''))), 'success', 'Update_User_-_Query_User_By_Id_After_Attributes_Update: User not found on SCIM server after update'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], ''))), null, 'SCIM verification failed: User not found on SCIM server after update provisioning'), concat('Scope execution failed. Failed action: ', variables('UpdateUserFailedActionName')))", + "overallResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true)), not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], '')))), 'PASSED', 'FAILED'), 'FAILED')", + "attributeMatchSummary": "@outputs('Update_User_-_Attribute_Match_Summary')" + } + } + }, + "runAfter": { + "Set_UpdateUser_CreatePhase_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('UpdateUserTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Update_Cleanup_Delete_User": { + "runAfter": { + "Update_User_Check_Create_Phase_Status": [ + "Succeeded", + "Failed", + "TimedOut", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Capture_Update_CreatePhase_Failed_Actions": { + "actions": { + "Get_Failed_CreatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Update_User_Verify_Creation_And_Provisioning')" + }, + "Filter_Failed_CreatePhase_Actions": { + "runAfter": { + "Get_Failed_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_CreatePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_UpdateUser_CreatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_CreatePhase_Actions')), 0), first(body('Filter_Failed_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_UpdateUser_CreatePhase_Failed_Action_Response": { + "runAfter": { + "Set_UpdateUser_CreatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_UpdateUser_CreatePhase_Status_Failed": { + "runAfter": { + "Set_UpdateUser_CreatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Update_User_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_UpdateUser_CreatePhase_Status_Success": { + "runAfter": { + "Update_User_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateUserScopeExecutionStatus", + "value": "Succeeded" + } }, + "Set_UpdateUser_CreatePhase_Output_Variables": { + "actions": { + "Set_UpdateUserTestOutputs": { + "type": "SetVariable", + "inputs": { + "name": "UpdateUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Update_User_Analyze_Create_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Update_User_Analyze_Create_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "type": "Scope" + } + }, + "else": { + "actions": { + "Skip_UpdateUser_Test": { "type": "Compose", - "inputs": "@if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'Join'), join(body('Select_Join_Values'), string(coalesce(first(body('Filter_Join_Separator'))?['value']?['name'], first(body('Filter_Join_Separator'))?['value']?['expression'], ' '))), if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'ToLower'), toLower(string(if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))), if(equals(coalesce(items('ForEach_SourceToTargetLookup')?['function'], ''), 'ToUpper'), toUpper(string(if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))), if(contains(triggerBody()?['initializationData']?['arrayProperties'], items('ForEach_SourceToTargetLookup')?['source']), first(triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']]), if(contains(items('ForEach_SourceToTargetLookup')?['source'], '.'), triggerBody()?['initializationData']?['updateUserBody']?[first(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['source'], '.'))], triggerBody()?['initializationData']?['updateUserBody']?[items('ForEach_SourceToTargetLookup')?['source']])))))" - }, - "Append_Matched_Attribute_Result": { + "inputs": { + "testName": "Update_User_Test", + "result": "SKIPPED", + "overallResult": "SKIPPED", + "errorDetails": null + } + }, + "Set_UpdateUser_Skipped": { "runAfter": { - "Compose_Expected_Value": [ - "Succeeded" - ] + "Skip_UpdateUser_Test": [ + "Succeeded" + ] }, - "type": "AppendToArrayVariable", + "type": "SetVariable", "inputs": { - "name": "matchedAttributesResult", - "value": { - "sourceAttribute": "@items('ForEach_SourceToTargetLookup')?['source']", - "targetAttribute": "@items('ForEach_SourceToTargetLookup')?['target']", - "expectedValue": "@outputs('Compose_Expected_Value')", - "actualValue": "@if(contains(items('ForEach_SourceToTargetLookup')?['target'], '[type eq'), if(contains(items('ForEach_SourceToTargetLookup')?['target'], '].'), first(body('Query_Filter_Array_By_Type'))?[last(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))], first(body('Query_Filter_Array_By_Type'))?['value']), if(and(contains(items('ForEach_SourceToTargetLookup')?['target'], '.'), not(contains(items('ForEach_SourceToTargetLookup')?['target'], ':'))), body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[first(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))]?[last(split(items('ForEach_SourceToTargetLookup')?['target'], '.'))], if(contains(items('ForEach_SourceToTargetLookup')?['target'], ':'), coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[substring(items('ForEach_SourceToTargetLookup')?['target'], 0, lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'))]?[substring(items('ForEach_SourceToTargetLookup')?['target'], add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1), sub(length(items('ForEach_SourceToTargetLookup')?['target']), add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1)))], body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[substring(items('ForEach_SourceToTargetLookup')?['target'], add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1), sub(length(items('ForEach_SourceToTargetLookup')?['target']), add(lastIndexOf(items('ForEach_SourceToTargetLookup')?['target'], ':'), 1)))]), body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?[items('ForEach_SourceToTargetLookup')?['target']])))" - } + "name": "UpdateUserTestOutputs", + "value": "@outputs('Skip_UpdateUser_Test')" } - } - }, - "else": { - "actions": {} } - } - }, - "runAfter": { - "Update_DoUntil_Wait_For_Attribute_Updates": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "type": "Foreach", - "runtimeConfiguration": { - "concurrency": { - "repetitions": 1 - } - } - }, - "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": { - "runAfter": { - "ForEach_SourceToTargetLookup": [ - "Succeeded" - ] - }, - "type": "Compose", - "inputs": "@variables('matchedAttributesResult')" - }, - "Update_User_-_Filter_Mismatched_Attributes": { - "runAfter": { - "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@coalesce(outputs('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]'))", - "where": "@not(equals(toLower(string(coalesce(item()?['expectedValue'], ''))), toLower(string(coalesce(item()?['actualValue'], '')))))" - } - }, - "Update_User_-_Attribute_Match_Summary": { - "runAfter": { - "Update_User_-_Filter_Mismatched_Attributes": [ - "Succeeded" - ] - }, - "type": "Compose", - "inputs": { - "totalAttributesVerified": "@length(coalesce(body('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]')))", - "allAttributesMatch": "@equals(length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))), 0)", - "mismatchCount": "@length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]')))", - "mismatchedAttributes": "@coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))", - "matchedCount": "@sub(length(coalesce(body('Update_User_-_Match_Expected_Attributes_After_Attributes_Update'), json('[]'))), length(coalesce(body('Update_User_-_Filter_Mismatched_Attributes'), json('[]'))))" } - }, - "Update_DoUntil_Poll_Update_Provisioning_Logs": { - "actions": { - "Update_Verify_Update_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Update_User_-_Create_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27&$top=1&$orderby=activityDateTime%20desc')", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Delay_Update_Provisioning_Logs_15s": { - "runAfter": { - "Update_Verify_Update_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Update_User_Test" + ] } - } - }, - "runAfter": { - "Update_User_-_Match_Expected_Attributes_After_Attributes_Update": [ - "Succeeded", - "Failed" - ] - }, - "expression": "@greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } + ] }, - "type": "Scope" - }, - "Capture_Update_UpdatePhase_Failed_Actions": { + "type": "If" + }, + "Delete_User_Test": { "actions": { - "Get_Failed_UpdatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Update_User_Verify_Update_And_Provisioning')" - }, - "Filter_Failed_UpdatePhase_Actions": { - "runAfter": { - "Get_Failed_UpdatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_UpdatePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_UpdateUser_UpdatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_UpdatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_UpdatePhase_Actions')), 0), first(body('Filter_Failed_UpdatePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_UpdateUser_UpdatePhase_Failed_Action_Response": { - "runAfter": { - "Set_UpdateUser_UpdatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_UpdatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_UpdatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_UpdateUser_UpdatePhase_Status_Failed": { - "runAfter": { - "Set_UpdateUser_UpdatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserScopeExecutionStatus", - "value": "Failed" + "Delete_User_Test_Actions": { + "actions": { + "DeleteUser_Phase1_Verify_Creation_And_Provisioning": { + "actions": { + "DeleteUser_Check_Delete_User_Exists_Initial": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Compose_DeleteUser_Body": { + "runAfter": { + "DeleteUser_Check_Delete_User_Exists_Initial": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['deleteUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['deleteUser']), 'displayName', triggerBody()?['initializationData']?['testUserNames']?['deleteUser']), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['deleteUserNickname'])" + }, + "DeleteUser_Create_Delete_User_Graph": { + "runAfter": { + "Compose_DeleteUser_Body": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_DeleteUser_Body')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DeleteUser_Assign_Delete_User_To_App": { + "runAfter": { + "DeleteUser_Create_Delete_User_Graph": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DeleteUser_DoUntil_Wait_For_User_Creation": { + "actions": { + "DeleteUser_Check_User_Synced": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DeleteUser_Delay_Creation_15s": { + "runAfter": { + "DeleteUser_Check_User_Synced": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DeleteUser_Assign_Delete_User_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DeleteUser_DoUntil_Poll_Provisioning_Logs": { + "actions": { + "DeleteUser_Verify_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DeleteUser_Create_Delete_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27&$orderby=activityDateTime%20desc&$top=1')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DeleteUser_Delay_Provisioning_Logs_15s": { + "runAfter": { + "DeleteUser_Verify_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DeleteUser_DoUntil_Wait_For_User_Creation": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "DeleteUser_Capture_Phase1_Failed_Actions": { + "actions": { + "DeleteUser_Get_Phase1_Failed_Actions": { + "type": "Compose", + "inputs": "@result('DeleteUser_Phase1_Verify_Creation_And_Provisioning')" + }, + "DeleteUser_Filter_Phase1_Failed_Actions": { + "runAfter": { + "DeleteUser_Get_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('DeleteUser_Get_Phase1_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "DeleteUser_Set_Phase1_Failed_Action_Name": { + "runAfter": { + "DeleteUser_Filter_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserFailedActionName", + "value": "@{if(greater(length(body('DeleteUser_Filter_Phase1_Failed_Actions')), 0), first(body('DeleteUser_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "DeleteUser_Set_Phase1_Failed_Action_Response": { + "runAfter": { + "DeleteUser_Set_Phase1_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserFailedActionResponse", + "value": "@if(greater(length(body('DeleteUser_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('DeleteUser_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "DeleteUser_Set_Phase1_Status_Failed": { + "runAfter": { + "DeleteUser_Set_Phase1_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "DeleteUser_Set_Phase1_Status_Success": { + "runAfter": { + "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "DeleteUser_Analyze_Phase1_Provisioning_Results": { + "runAfter": { + "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "DeleteUser_Capture_Phase1_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "DeleteUser_Set_Phase1_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DeleteUser_Verify_Provisioning_Logs: ', coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DeleteUser_Verify_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0), false), 'success', 'DeleteUser_Check_User_Synced: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('DeleteUser_Check_User_Synced')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DeleteUserFailedActionName')))", + "overallResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "DeleteUser_Set_Phase1_Output_Variables": { + "actions": { + "DeleteUser_Set_DeleteUserTestOutputs_Phase1": { + "type": "SetVariable", + "inputs": { + "name": "DeleteUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "DeleteUser_Analyze_Phase1_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "DeleteUser_Check_Phase1_Status": { + "actions": { + "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": { + "actions": { + "DeleteUser_Query_User_By_Id": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DeleteUser_Delete_User_From_Graph": { + "runAfter": { + "DeleteUser_Query_User_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DeleteUser_Purge_From_Recycle_Bin": { + "runAfter": { + "DeleteUser_Delay_Before_Purge": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/directory/deletedItems/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DeleteUser_DoUntil_Wait_For_User_Deletion": { + "actions": { + "DeleteUser_Check_User_Deleted": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('DeleteUser_Query_User_By_Id')?['Resources'])?['id'], ''))}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DeleteUser_Delay_Deletion_15s": { + "runAfter": { + "DeleteUser_Check_User_Deleted": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DeleteUser_Purge_From_Recycle_Bin": [ + "Succeeded" + ] + }, + "expression": "@equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DeleteUser_DoUntil_Poll_Delete_Provisioning_Logs": { + "actions": { + "DeleteUser_Verify_Delete_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DeleteUser_Create_Delete_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Delete%27&$orderby=activityDateTime%20desc&$top=1')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + }, + "runAfter": {} + }, + "DeleteUser_Delay": { + "runAfter": { + "DeleteUser_Verify_Delete_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DeleteUser_DoUntil_Wait_For_User_Deletion": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "DeleteUser_Delay_Before_Purge": { + "type": "Wait", + "inputs": { + "interval": { + "count": 2, + "unit": "Minute" + } + }, + "runAfter": { + "DeleteUser_Delete_User_From_Graph": [ + "Succeeded" + ] + } + } + }, + "type": "Scope" + }, + "DeleteUser_Capture_Phase2_Failed_Actions": { + "actions": { + "DeleteUser_Get_Phase2_Failed_Actions": { + "type": "Compose", + "inputs": "@result('DeleteUser_Phase2_Verify_Deletion_And_Provisioning')" + }, + "DeleteUser_Filter_Phase2_Failed_Actions": { + "runAfter": { + "DeleteUser_Get_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('DeleteUser_Get_Phase2_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "DeleteUser_Set_Phase2_Failed_Action_Name": { + "runAfter": { + "DeleteUser_Filter_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserFailedActionName", + "value": "@{if(greater(length(body('DeleteUser_Filter_Phase2_Failed_Actions')), 0), first(body('DeleteUser_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "DeleteUser_Set_Phase2_Failed_Action_Response": { + "runAfter": { + "DeleteUser_Set_Phase2_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserFailedActionResponse", + "value": "@if(greater(length(body('DeleteUser_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('DeleteUser_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "DeleteUser_Set_Phase2_Status_Failed": { + "runAfter": { + "DeleteUser_Set_Phase2_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "DeleteUser_Set_Phase2_Status_Success": { + "runAfter": { + "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "DeleteUser_Analyze_Phase2_Provisioning_Results": { + "runAfter": { + "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "DeleteUser_Capture_Phase2_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "DeleteUser_Set_Phase2_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "deleteLogsCount": "@length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]')))", + "hasDeleteLogs": "@greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", + "deleteStatus": "@if(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DeleteUser_Verify_Delete_Provisioning_Logs: ', coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DeleteUser_Verify_Delete_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), not(equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404), 'success', concat('User still present on SCIM server (statusCode: ', string(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0)), ')')), variables('DeleteUserFailedActionResponse'))", + "scimCheckErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404), null, concat('User still exists on SCIM server. statusCode: ', string(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0)), ', active: ', string(coalesce(body('DeleteUser_Check_User_Deleted')?['active'], 'N/A')))), concat('Scope execution status: ', variables('DeleteUserScopeExecutionStatus')))", + "overallResult": "@if(and(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), and(and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404))), 'PASSED', 'FAILED')" + } + }, + "DeleteUser_Set_Phase2_Output_Variables": { + "actions": { + "DeleteUser_Set_DeleteUserTestOutputs_Phase2": { + "type": "SetVariable", + "inputs": { + "name": "DeleteUserTestOutputs", + "value": { + "result": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Delete Phase] ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Delete Phase] Provisioning: ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "DeleteUser_Analyze_Phase2_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "DeleteUser_Set_Phase1_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('DeleteUserTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + } + }, + "type": "Scope" } - } - }, - "runAfter": { - "Update_User_Verify_Update_And_Provisioning": [ - "Failed" - ] }, - "type": "Scope" - }, - "Set_UpdateUser_UpdatePhase_Status_Success": { - "runAfter": { - "Update_User_Verify_Update_And_Provisioning": [ - "Succeeded" - ] + "else": { + "actions": {} }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Set_UpdateUser_UpdatePhase_Output_Variables": { - "actions": { - "Set_UpdateUserTest_UpdatePhase_Outputs": { - "type": "SetVariable", - "inputs": { - "name": "UpdateUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Update Phase] ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Update Phase] Provisioning: ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Update_Analyze_Update_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Analyze_Update_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + "expression": { + "and": [ + { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Delete_User_Test" + ] + } + ] } - } - } - } - }, - "runAfter": { - "Update_Analyze_Update_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "Update_Analyze_Update_Provisioning_Results": { - "runAfter": { - "Update_User_Verify_Update_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Update_UpdatePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_UpdateUser_UpdatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] + ] }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), if(equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true), 'success', concat('Update_User_-_Attribute_Match_Summary: Attribute mismatch: ', string(outputs('Update_User_-_Attribute_Match_Summary')?['mismatchCount']), ' attributes failed')), concat('Update_Verify_Update_Provisioning_Logs: ', coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Verify_Update_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], if(not(equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true)), outputs('Update_User_-_Attribute_Match_Summary')?['mismatchedAttributes'], null)), variables('UpdateUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], ''))), 'success', 'Update_User_-_Query_User_By_Id_After_Attributes_Update: User not found on SCIM server after update'), concat('Failed Action: ', variables('UpdateUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], ''))), null, 'SCIM verification failed: User not found on SCIM server after update provisioning'), concat('Scope execution failed. Failed action: ', variables('UpdateUserFailedActionName')))", - "overallResult": "@if(equals(variables('UpdateUserScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Verify_Update_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(outputs('Update_User_-_Attribute_Match_Summary')?['allAttributesMatch'], true)), not(empty(coalesce(body('Update_User_-_Query_User_By_Id_After_Attributes_Update')?['id'], '')))), 'PASSED', 'FAILED'), 'FAILED')", - "attributeMatchSummary": "@outputs('Update_User_-_Attribute_Match_Summary')" - } - } - }, - "runAfter": { - "Set_UpdateUser_CreatePhase_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} + "type": "If" }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('UpdateUserTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Update_Cleanup_Delete_User": { - "runAfter": { - "Update_User_Check_Create_Phase_Status": [ - "Succeeded", - "Failed", - "TimedOut", - "Skipped" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Update_User_-_Create_User_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Capture_Update_CreatePhase_Failed_Actions": { - "actions": { - "Get_Failed_CreatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Update_User_Verify_Creation_And_Provisioning')" - }, - "Filter_Failed_CreatePhase_Actions": { - "runAfter": { - "Get_Failed_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_CreatePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_UpdateUser_CreatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_CreatePhase_Actions')), 0), first(body('Filter_Failed_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_UpdateUser_CreatePhase_Failed_Action_Response": { - "runAfter": { - "Set_UpdateUser_CreatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_UpdateUser_CreatePhase_Status_Failed": { - "runAfter": { - "Set_UpdateUser_CreatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Update_User_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_UpdateUser_CreatePhase_Status_Success": { - "runAfter": { - "Update_User_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Set_UpdateUser_CreatePhase_Output_Variables": { - "actions": { - "Set_UpdateUserTestOutputs": { - "type": "SetVariable", - "inputs": { - "name": "UpdateUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Update_User_Analyze_Create_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_User_Analyze_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Update_User_Analyze_Create_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "type": "Scope" - } - }, - "else": { - "actions": { - "Skip_UpdateUser_Test": { - "type": "Compose", - "inputs": { - "testName": "Update_User_Test", - "result": "SKIPPED", - "overallResult": "SKIPPED", - "errorDetails": null - } - }, - "Set_UpdateUser_Skipped": { - "runAfter": { - "Skip_UpdateUser_Test": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateUserTestOutputs", - "value": "@outputs('Skip_UpdateUser_Test')" - } - } - } - }, - "expression": { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Update_User_Test" - ] - } - ] - }, - "type": "If" - }, - "Delete_User_Test": { - "actions": { - "Delete_User_Test_Actions": { - "actions": { - "DeleteUser_Phase1_Verify_Creation_And_Provisioning": { - "actions": { - "DeleteUser_Check_Delete_User_Exists_Initial": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DeleteUser_Create_Delete_User_Graph": { - "runAfter": { - "DeleteUser_Check_Delete_User_Exists_Initial": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{triggerBody()?['initializationData']?['testUserNames']?['deleteUser']}", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['deleteUserNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['deleteUser']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['deleteUser']}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DeleteUser_Assign_Delete_User_To_App": { - "runAfter": { - "DeleteUser_Create_Delete_User_Graph": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DeleteUser_DoUntil_Wait_For_User_Creation": { + "User_Update_Manager_Test": { "actions": { - "DeleteUser_Check_User_Synced": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DeleteUser_Delay_Creation_15s": { - "runAfter": { - "DeleteUser_Check_User_Synced": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + "User_Update_Manager_Test_Actions": { + "actions": { + "Phase1_Create_Users_And_Initial_Sync": { + "actions": { + "Step1_Check_User_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Compose_ManagerTest_UserBody": { + "runAfter": { + "Step1_Check_User_Exists": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['managerUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['managerUser']), 'displayName', triggerBody()?['initializationData']?['testUserNames']?['managerUser']), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['managerUserNickname'])" + }, + "Step2_Create_Test_User": { + "runAfter": { + "Compose_ManagerTest_UserBody": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_ManagerTest_UserBody')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Compose_ManagerTest_InitialManagerBody": { + "runAfter": { + "Step2_Create_Test_User": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial']), 'displayName', 'Initial Manager User'), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['managerUserInitialNickname'])" + }, + "Step3_Create_Initial_Manager": { + "runAfter": { + "Compose_ManagerTest_InitialManagerBody": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_ManagerTest_InitialManagerBody')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Compose_ManagerTest_NewManagerBody": { + "runAfter": { + "Step3_Create_Initial_Manager": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['managerUserNew']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['managerUserNew']), 'displayName', 'New Manager User'), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['managerUserNewNickname'])" + }, + "Step4_Create_New_Manager": { + "runAfter": { + "Compose_ManagerTest_NewManagerBody": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_ManagerTest_NewManagerBody')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Step5_Assign_Test_User_To_App": { + "runAfter": { + "Step4_Create_New_Manager": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Step2_Create_Test_User')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Step6_Assign_Initial_Manager_To_App": { + "runAfter": { + "Step5_Assign_Test_User_To_App": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Step3_Create_Initial_Manager')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Step7_Assign_New_Manager_To_App": { + "runAfter": { + "Step6_Assign_Initial_Manager_To_App": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Step4_Create_New_Manager')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DoUntil_Poll_All_Users_Created": { + "actions": { + "SCIM_Query_Test_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "SCIM_Query_Initial_Manager": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "SCIM_Query_New_Manager": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUserNew'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_15s": { + "runAfter": { + "SCIM_Query_Test_User": [ + "Succeeded", + "Failed" + ], + "SCIM_Query_Initial_Manager": [ + "Succeeded", + "Failed" + ], + "SCIM_Query_New_Manager": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Step7_Assign_New_Manager_To_App": [ + "Succeeded" + ] + }, + "expression": "@and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Initial_Provisioning_Logs": { + "actions": { + "Verify_Test_User_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Verify_Initial_Manager_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step3_Create_Initial_Manager')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Verify_New_Manager_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step4_Create_New_Manager')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "ManagerTest_Delay_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Test_User_Provisioning_Logs": [ + "Succeeded", + "Failed" + ], + "Verify_Initial_Manager_Provisioning_Logs": [ + "Succeeded", + "Failed" + ], + "Verify_New_Manager_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Poll_All_Users_Created": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "Capture_Phase1_Failed_Actions": { + "actions": { + "Get_Phase1_Failed_Actions": { + "type": "Compose", + "inputs": "@result('Phase1_Create_Users_And_Initial_Sync')" + }, + "Filter_Phase1_Failed_Actions": { + "runAfter": { + "Get_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Phase1_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Phase1_Failed_Action_Name": { + "runAfter": { + "Filter_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionName", + "value": "@{if(greater(length(body('Filter_Phase1_Failed_Actions')), 0), first(body('Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Phase1_Failed_Action_Response": { + "runAfter": { + "Set_Phase1_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionResponse", + "value": "@if(greater(length(body('Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_Phase1_Status_Failed": { + "runAfter": { + "Set_Phase1_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Phase1_Create_Users_And_Initial_Sync": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_Phase1_Status_Success": { + "runAfter": { + "Phase1_Create_Users_And_Initial_Sync": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Analyze_Phase1_Provisioning_Results": { + "runAfter": { + "Phase1_Create_Users_And_Initial_Sync": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Phase1_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_Phase1_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), if(and(and(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'success', concat('Verify_Test_User_Provisioning_Logs: ', coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', Verify_Initial_Manager_Provisioning_Logs: ', coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', Verify_New_Manager_Provisioning_Logs: ', coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), concat('Verify_Test_User_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'), ', Verify_Initial_Manager_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'), ', Verify_New_Manager_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'))), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(or(or(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), json(concat('{\"testUser\":', string(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"initialManager\":', string(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"newManager\":', string(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('ManagerTestFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0)), 'success', concat('SCIM users not found: TestUser=', string(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]')))), ', InitialManager=', string(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]')))), ', NewManager=', string(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]')))))), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0)), null, concat('SCIM verification failed: Not all users found on SCIM server. TestUser resources: ', string(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]')))), ', InitialManager resources: ', string(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]')))), ', NewManager resources: ', string(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", + "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), not(or(or(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')))), and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "Set_Phase1_Output_Variables": { + "actions": { + "Set_ManagerTestOutputs_Phase1": { + "type": "SetVariable", + "inputs": { + "name": "ManagerTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Analyze_Phase1_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Analyze_Phase1_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "Cleanup_Delete_Test_User": { + "runAfter": { + "Check_Phase3_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Cleanup_Delete_Initial_Manager": { + "runAfter": { + "Cleanup_Delete_Test_User": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Cleanup_Delete_New_Manager": { + "runAfter": { + "Cleanup_Delete_Initial_Manager": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Check_Phase1_Status": { + "actions": { + "Phase2_Set_Manager": { + "actions": { + "Step1_Set_Manager_Relationship": { + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", + "method": "PUT", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + }, + "runAfter": { + "Capture_Phase2_Start_Time": [ + "Succeeded" + ] + } + }, + "DoUntil_Wait_For_Manager_Set": { + "actions": { + "SCIM_Verify_Manager_Set": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Manager_Set_15s": { + "runAfter": { + "SCIM_Verify_Manager_Set": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Step1_Set_Manager_Relationship": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Set_Manager_Provisioning_Logs": { + "actions": { + "Verify_Set_Manager_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase2_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delay_Set_Manager_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Set_Manager_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Wait_For_Manager_Set": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "expression": "@and(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Capture_Phase2_Start_Time": { + "type": "Compose", + "inputs": "@utcNow()", + "runAfter": {} + } + }, + "type": "Scope" + }, + "Capture_Phase2_Failed_Actions": { + "actions": { + "Get_Phase2_Failed_Actions": { + "type": "Compose", + "inputs": "@result('Phase2_Set_Manager')" + }, + "Filter_Phase2_Failed_Actions": { + "runAfter": { + "Get_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Phase2_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Phase2_Failed_Action_Name": { + "runAfter": { + "Filter_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionName", + "value": "@{if(greater(length(body('Filter_Phase2_Failed_Actions')), 0), first(body('Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Phase2_Failed_Action_Response": { + "runAfter": { + "Set_Phase2_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionResponse", + "value": "@{if(greater(length(body('Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" + } + }, + "Set_Phase2_Status_Failed": { + "runAfter": { + "Set_Phase2_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Phase2_Set_Manager": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_Phase2_Status_Success": { + "runAfter": { + "Phase2_Set_Manager": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Analyze_Phase2_Provisioning_Results": { + "runAfter": { + "Phase2_Set_Manager": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Phase2_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_Phase2_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "setManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", + "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", + "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), 'success', concat('Verify_Set_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), ' (expected ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ')')), 'Verify_Set_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. Actual: newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), '. Error: ', coalesce(string(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for set manager operation. Phase start: ', outputs('Capture_Phase2_Start_Time'))), variables('ManagerTestFailedActionResponse'))", + "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), 'success', concat('SCIM manager verification failed: expected manager ID ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ' in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), null, concat('SCIM verification failed: Manager not correctly set. Expected manager SCIM ID: ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. SCIM response resources count: ', string(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", + "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']), 'matched', concat('Provisioning log manager mismatch: expected newValue=', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ', but found newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" + } + }, + "Set_Phase2_Output_Variables": { + "actions": { + "Set_ManagerTestOutputs_Phase2": { + "type": "SetVariable", + "inputs": { + "name": "ManagerTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Set Manager Phase] ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Set Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Analyze_Phase2_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Analyze_Phase2_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Set_Phase1_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('ManagerTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Check_Phase2_Status": { + "actions": { + "Phase3_Change_Manager": { + "actions": { + "Step1_Change_Manager_Relationship": { + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", + "method": "PUT", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + }, + "runAfter": { + "Capture_Phase3_Start_Time": [ + "Succeeded" + ] + } + }, + "DoUntil_Wait_For_Manager_Change": { + "actions": { + "SCIM_Verify_Manager_Change": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Manager_Change_15s": { + "runAfter": { + "SCIM_Verify_Manager_Change": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Step1_Change_Manager_Relationship": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Change_Manager_Provisioning_Logs": { + "actions": { + "Verify_Change_Manager_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase3_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delay_Change_Manager_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Change_Manager_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Wait_For_Manager_Change": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "expression": "@and(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Capture_Phase3_Start_Time": { + "type": "Compose", + "inputs": "@utcNow()", + "runAfter": {} + } + }, + "type": "Scope" + }, + "Capture_Phase3_Failed_Actions": { + "actions": { + "Get_Phase3_Failed_Actions": { + "type": "Compose", + "inputs": "@result('Phase3_Change_Manager')" + }, + "Filter_Phase3_Failed_Actions": { + "runAfter": { + "Get_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Phase3_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Phase3_Failed_Action_Name": { + "runAfter": { + "Filter_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionName", + "value": "@{if(greater(length(body('Filter_Phase3_Failed_Actions')), 0), first(body('Filter_Phase3_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Phase3_Failed_Action_Response": { + "runAfter": { + "Set_Phase3_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionResponse", + "value": "@{if(greater(length(body('Filter_Phase3_Failed_Actions')), 0), coalesce(first(body('Filter_Phase3_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" + } + }, + "Set_Phase3_Status_Failed": { + "runAfter": { + "Set_Phase3_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Phase3_Change_Manager": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_Phase3_Status_Success": { + "runAfter": { + "Phase3_Change_Manager": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Analyze_Phase3_Provisioning_Results": { + "runAfter": { + "Phase3_Change_Manager": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Phase3_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_Phase3_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "changeManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", + "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", + "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), 'success', concat('Verify_Change_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), ' (expected ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ')')), 'Verify_Change_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. Actual: newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), '. Error: ', coalesce(string(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for change manager operation. Phase start: ', outputs('Capture_Phase3_Start_Time'))), variables('ManagerTestFailedActionResponse'))", + "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), 'success', concat('SCIM manager verification failed: expected new manager ID ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ' in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), null, concat('SCIM verification failed: Manager not correctly changed. Expected new manager SCIM ID: ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. SCIM response resources count: ', string(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", + "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']), 'matched', concat('Provisioning log manager mismatch: expected newValue=', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ', but found newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), '. This may indicate stale provisioning logs from a previous phase.')), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" + } + }, + "Set_Phase3_Output_Variables": { + "actions": { + "Set_ManagerTestOutputs_Phase3": { + "type": "SetVariable", + "inputs": { + "name": "ManagerTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Change Manager Phase] ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Change Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Analyze_Phase3_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Analyze_Phase3_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Check_Phase1_Status": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('ManagerTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Check_Phase3_Status": { + "actions": { + "Phase4_Remove_Manager": { + "actions": { + "Step1_Remove_Manager_Relationship": { + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", + "method": "DELETE", + "headers": { + "Content-Type": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + }, + "runAfter": { + "Capture_Phase4_Start_Time": [ + "Succeeded" + ] + } + }, + "DoUntil_Wait_For_Manager_Remove": { + "actions": { + "SCIM_Verify_Manager_Remove": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Manager_Remove_15s": { + "runAfter": { + "SCIM_Verify_Manager_Remove": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Step1_Remove_Manager_Relationship": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'])))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Remove_Manager_Provisioning_Logs": { + "actions": { + "Verify_Remove_Manager_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase4_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delay_Remove_Manager_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Remove_Manager_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Wait_For_Manager_Remove": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "expression": "@and(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Capture_Phase4_Start_Time": { + "type": "Compose", + "inputs": "@utcNow()", + "runAfter": {} + } + }, + "type": "Scope" + }, + "Capture_Phase4_Failed_Actions": { + "actions": { + "Get_Phase4_Failed_Actions": { + "type": "Compose", + "inputs": "@result('Phase4_Remove_Manager')" + }, + "Filter_Phase4_Failed_Actions": { + "runAfter": { + "Get_Phase4_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Phase4_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Phase4_Failed_Action_Name": { + "runAfter": { + "Filter_Phase4_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionName", + "value": "@{if(greater(length(body('Filter_Phase4_Failed_Actions')), 0), first(body('Filter_Phase4_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Phase4_Failed_Action_Response": { + "runAfter": { + "Set_Phase4_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestFailedActionResponse", + "value": "@{if(greater(length(body('Filter_Phase4_Failed_Actions')), 0), coalesce(first(body('Filter_Phase4_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" + } + }, + "Set_Phase4_Status_Failed": { + "runAfter": { + "Set_Phase4_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Phase4_Remove_Manager": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_Phase4_Status_Success": { + "runAfter": { + "Phase4_Remove_Manager": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "ManagerTestScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Analyze_Phase4_Provisioning_Results": { + "runAfter": { + "Phase4_Remove_Manager": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Phase4_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_Phase4_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "removeManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", + "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", + "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'))), 'success', concat('Verify_Remove_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), ' (expected null)')), 'Verify_Remove_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=null. Actual: newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), '. Error: ', coalesce(string(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for remove manager operation. Phase start: ', outputs('Capture_Phase4_Start_Time'))), variables('ManagerTestFailedActionResponse'))", + "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], ''))))), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], '')))), 'success', concat('SCIM manager verification failed: manager still present in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], '')))), null, concat('SCIM verification failed: Manager not correctly removed. SCIM response still contains manager value: ', coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], 'UNKNOWN'))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", + "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')), 'matched', concat('Provisioning log manager mismatch: expected newValue=null, but found newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" + } + }, + "Set_Phase4_Output_Variables": { + "actions": { + "Set_ManagerTestOutputs_Phase4": { + "type": "SetVariable", + "inputs": { + "name": "ManagerTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Remove Manager Phase] ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Remove Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Analyze_Phase4_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Analyze_Phase4_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Check_Phase2_Status": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('ManagerTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + } + }, + "type": "Scope" } - } }, - "runAfter": { - "DeleteUser_Assign_Delete_User_To_App": [ - "Succeeded" - ] + "else": { + "actions": {} }, - "expression": "@greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT20M" + "expression": { + "and": [ + { + "equals": [ + "@triggerBody()?['initializationData']?['isManagerAttributeSupported']", + true + ] + }, + { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "User_Update_Manager_Test" + ] + } + ] + } + ] }, - "type": "Until" - }, - "DeleteUser_DoUntil_Poll_Provisioning_Logs": { + "type": "If" + }, + "Disable_User_Test": { "actions": { - "DeleteUser_Verify_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DeleteUser_Create_Delete_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27&$orderby=activityDateTime%20desc&$top=1')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DeleteUser_Delay_Provisioning_Logs_15s": { - "runAfter": { - "DeleteUser_Verify_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + "DisableUserTest_Disable_User_Test_Actions": { + "actions": { + "DisableUserTest_Phase1_Create_User_And_Verify": { + "actions": { + "DisableUserTest_Disable_Step1_Check_User_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}", + "Content-Type": "@{parameters('scimContentType')}" + } + } + }, + "Compose_DisableUser_Body": { + "runAfter": { + "DisableUserTest_Disable_Step1_Check_User_Exists": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['disableUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['disableUser']), 'displayName', triggerBody()?['initializationData']?['testUserNames']?['disableUser']), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['disableUserNickname'])" + }, + "DisableUserTest_Disable_Step2_Create_User": { + "runAfter": { + "Compose_DisableUser_Body": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_DisableUser_Body')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DisableUserTest_Disable_Assign_User_To_App": { + "runAfter": { + "DisableUserTest_Disable_Step2_Create_User": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DisableUserTest_Disable_DoUntil_Wait_For_User_Creation": { + "actions": { + "DisableUserTest_Disable_Step3_Fetch_User_By_Filter": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DisableUserTest_Disable_Delay_Creation_15s": { + "runAfter": { + "DisableUserTest_Disable_Step3_Fetch_User_By_Filter": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DisableUserTest_Disable_Assign_User_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Step3_Fetch_User_By_Filter')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DisableUserTest_Disable_Create_Poll_Provisioning_Logs": { + "actions": { + "DisableUserTest_Disable_Create_Check_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DisableUserTest_Disable_Step2_Create_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DisableUserTest_Disable_Create_Polling_Wait": { + "runAfter": { + "DisableUserTest_Disable_Create_Check_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DisableUserTest_Disable_DoUntil_Wait_For_User_Creation": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "DisableUserTest_Capture_Phase1_Failed_Actions": { + "actions": { + "DisableUserTest_Get_Phase1_Failed_Actions": { + "type": "Compose", + "inputs": "@result('DisableUserTest_Phase1_Create_User_And_Verify')" + }, + "DisableUserTest_Filter_Phase1_Failed_Actions": { + "runAfter": { + "DisableUserTest_Get_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('DisableUserTest_Get_Phase1_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "DisableUserTest_Set_Phase1_Failed_Action_Name": { + "runAfter": { + "DisableUserTest_Filter_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserFailedActionName", + "value": "@{if(greater(length(body('DisableUserTest_Filter_Phase1_Failed_Actions')), 0), first(body('DisableUserTest_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "DisableUserTest_Set_Phase1_Failed_Action_Response": { + "runAfter": { + "DisableUserTest_Set_Phase1_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserFailedActionResponse", + "value": "@if(greater(length(body('DisableUserTest_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('DisableUserTest_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "DisableUserTest_Set_Phase1_Status_Failed": { + "runAfter": { + "DisableUserTest_Set_Phase1_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "DisableUserTest_Phase1_Create_User_And_Verify": [ + "Failed" + ] + }, + "type": "Scope" + }, + "DisableUserTest_Set_Phase1_Status_Success": { + "runAfter": { + "DisableUserTest_Phase1_Create_User_And_Verify": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "DisableUserTest_Analyze_Phase1_Provisioning_Results": { + "runAfter": { + "DisableUserTest_Phase1_Create_User_And_Verify": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "DisableUserTest_Capture_Phase1_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "DisableUserTest_Set_Phase1_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DisableUserTest_Disable_Create_Check_Provisioning_Logs: ', coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DisableUserTest_Disable_Create_Check_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DisableUserFailedActionResponse'))", + "overallResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "DisableUserTest_Set_Phase1_Output_Variables": { + "actions": { + "DisableUserTest_Set_DisableUserTestOutputs_Phase1": { + "type": "SetVariable", + "inputs": { + "name": "DisableUserTestOutputs", + "value": { + "result": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', if(startsWith(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "N/A", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": null + } + } + } + } + }, + "runAfter": { + "DisableUserTest_Analyze_Phase1_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "DisableUserTest_Check_Phase1_Status": { + "actions": { + "DisableUserTest_Phase2_Disable_User_And_Verify": { + "actions": { + "DisableUserTest_Disable_Step4_Query_User_By_Id": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DisableUserTest_Disable_Step5_Disable_User": { + "runAfter": { + "DisableUserTest_Disable_Step4_Query_User_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", + "method": "PATCH", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "accountEnabled": false + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DisableUserTest_Disable_DoUntil_Wait_For_User_Disable": { + "actions": { + "DisableUserTest_Disable_Step6_Check_User_After_Disable": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "DisableUserTest_Disable_Delay_Final_15s": { + "runAfter": { + "DisableUserTest_Disable_Step6_Check_User_After_Disable": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DisableUserTest_Disable_Step5_Disable_User": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DisableUserTest_Disable_DoUntil_Poll_Disable_Provisioning_Logs": { + "actions": { + "DisableUserTest_Disable_Verify_Disable_Provisioning_Logs": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DisableUserTest_Disable_Step2_Create_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27', 'Disable', '%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "DisableUserTest_Disable_Delay_Disable_Provisioning_Logs_15s": { + "runAfter": { + "DisableUserTest_Disable_Verify_Disable_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DisableUserTest_Disable_DoUntil_Wait_For_User_Disable": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "DisableUserTest_Capture_Phase2_Failed_Actions": { + "actions": { + "DisableUserTest_Get_Phase2_Failed_Actions": { + "type": "Compose", + "inputs": "@result('DisableUserTest_Phase2_Disable_User_And_Verify')" + }, + "DisableUserTest_Filter_Phase2_Failed_Actions": { + "runAfter": { + "DisableUserTest_Get_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('DisableUserTest_Get_Phase2_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "DisableUserTest_Set_Phase2_Failed_Action_Name": { + "runAfter": { + "DisableUserTest_Filter_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserFailedActionName", + "value": "@{if(greater(length(body('DisableUserTest_Filter_Phase2_Failed_Actions')), 0), first(body('DisableUserTest_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "DisableUserTest_Set_Phase2_Failed_Action_Response": { + "runAfter": { + "DisableUserTest_Set_Phase2_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserFailedActionResponse", + "value": "@if(greater(length(body('DisableUserTest_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('DisableUserTest_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "DisableUserTest_Set_Phase2_Status_Failed": { + "runAfter": { + "DisableUserTest_Set_Phase2_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "DisableUserTest_Phase2_Disable_User_And_Verify": [ + "Failed" + ] + }, + "type": "Scope" + }, + "DisableUserTest_Set_Phase2_Status_Success": { + "runAfter": { + "DisableUserTest_Phase2_Disable_User_And_Verify": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DisableUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "DisableUserTest_Analyze_Phase2_Provisioning_Results": { + "runAfter": { + "DisableUserTest_Phase2_Disable_User_And_Verify": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "DisableUserTest_Capture_Phase2_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "DisableUserTest_Set_Phase2_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs: ', coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DisableUserTest_Disable_Verify_Disable_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DisableUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false)), 'success', 'DisableUserTest: User still active on SCIM server after disable'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false)), null, concat('Action: DisableUserTest_Disable_Step6_Check_User_After_Disable - User not disabled on SCIM server. Resources count: ', string(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]')))), ', active: ', string(coalesce(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DisableUserFailedActionName')))", + "overallResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false))), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "DisableUserTest_Set_Phase2_Output_Variables": { + "actions": { + "DisableUserTest_Set_DisableUserTestOutputs_Phase2": { + "type": "SetVariable", + "inputs": { + "name": "DisableUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Disable Phase] ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Disable Phase] Provisioning: ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "DisableUserTest_Analyze_Phase2_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "DisableUserTest_Set_Phase1_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('DisableUserTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "DisableUserTest_Cleanup_Delete_User": { + "runAfter": { + "DisableUserTest_Check_Phase1_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + }, + "type": "Scope" } - } }, - "runAfter": { - "DeleteUser_DoUntil_Wait_For_User_Creation": [ - "Succeeded", - "Failed", - "TimedOut" - ] + "else": { + "actions": {} }, - "expression": "@greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Disable_User_Test" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + } + ] }, - "type": "Until" - } + "type": "If" }, - "type": "Scope" - }, - "DeleteUser_Capture_Phase1_Failed_Actions": { - "actions": { - "DeleteUser_Get_Phase1_Failed_Actions": { - "type": "Compose", - "inputs": "@result('DeleteUser_Phase1_Verify_Creation_And_Provisioning')" - }, - "DeleteUser_Filter_Phase1_Failed_Actions": { - "runAfter": { - "DeleteUser_Get_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('DeleteUser_Get_Phase1_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "DeleteUser_Set_Phase1_Failed_Action_Name": { - "runAfter": { - "DeleteUser_Filter_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserFailedActionName", - "value": "@{if(greater(length(body('DeleteUser_Filter_Phase1_Failed_Actions')), 0), first(body('DeleteUser_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "DeleteUser_Set_Phase1_Failed_Action_Response": { - "runAfter": { - "DeleteUser_Set_Phase1_Failed_Action_Name": [ - "Succeeded" - ] + "Restore_User_Test": { + "actions": { + "RestoreUserTest_Actions": { + "type": "Scope", + "actions": { + "RestoreUserTest_Phase1_Create_And_Assign": { + "type": "Scope", + "actions": { + "RestoreUserTest_SCIM_Pre_Check": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Compose_RestoreUser_Body": { + "runAfter": { + "RestoreUserTest_SCIM_Pre_Check": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['restoreUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['restoreUser']), 'displayName', triggerBody()?['initializationData']?['testUserNames']?['restoreUser']), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['restoreUserNickname'])" + }, + "RestoreUserTest_Create_Entra_User": { + "runAfter": { + "Compose_RestoreUser_Body": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_RestoreUser_Body')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_Set_EntraUserId": { + "runAfter": { + "RestoreUserTest_Create_Entra_User": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "restoreEntraUserId", + "value": "@{body('RestoreUserTest_Create_Entra_User')?['id']}" + } + }, + "RestoreUserTest_Assign_AppRole": { + "runAfter": { + "RestoreUserTest_Set_EntraUserId": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'), '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{variables('restoreEntraUserId')}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_DoUntil_Verify_SCIM_Create": { + "runAfter": { + "RestoreUserTest_Assign_AppRole": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@equals(coalesce(body('RestoreUserTest_SCIM_Get_Create')?['totalResults'], 0), 1)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "actions": { + "RestoreUserTest_SCIM_Get_Create": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreUserTest_Wait_Create_30s": { + "runAfter": { + "RestoreUserTest_SCIM_Get_Create": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + }, + "RestoreUserTest_Set_OriginalScimId": { + "runAfter": { + "RestoreUserTest_DoUntil_Verify_SCIM_Create": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "restoreOriginalScimId", + "value": "@{coalesce(first(body('RestoreUserTest_SCIM_Get_Create')?['Resources'])?['id'], '')}" + } + }, + "RestoreUserTest_DoUntil_Verify_Create_ProvisioningLog": { + "runAfter": { + "RestoreUserTest_Set_OriginalScimId": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@equals(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], ''), 'success')", + "limit": { + "count": 20, + "timeout": "PT10M" + }, + "actions": { + "RestoreUserTest_Get_Create_Log": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20''', triggerBody()?['initializationData']?['syncJobId'], '''%20and%20servicePrincipal/id%20eq%20''', parameters('servicePrincipalId'), '''%20and%20sourceIdentity/id%20eq%20''', variables('restoreEntraUserId'), '''%20and%20provisioningAction%20eq%20''Create''')}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_Wait_Create_Log_30s": { + "runAfter": { + "RestoreUserTest_Get_Create_Log": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + } + } + }, + "RestoreUserTest_Capture_Phase1_Failed_Actions": { + "actions": { + "RestoreUserTest_Get_Phase1_Failed_Actions": { + "type": "Compose", + "inputs": "@result('RestoreUserTest_Phase1_Create_And_Assign')" + }, + "RestoreUserTest_Filter_Phase1_Failed_Actions": { + "runAfter": { + "RestoreUserTest_Get_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('RestoreUserTest_Get_Phase1_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "RestoreUserTest_Set_Phase1_Failed_Action_Name": { + "runAfter": { + "RestoreUserTest_Filter_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionName", + "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase1_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "RestoreUserTest_Set_Phase1_Failed_Action_Response": { + "runAfter": { + "RestoreUserTest_Set_Phase1_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionResponse", + "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "RestoreUserTest_Set_Phase1_Status_Failed": { + "runAfter": { + "RestoreUserTest_Set_Phase1_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "RestoreUserTest_Phase1_Create_And_Assign": [ + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreUserTest_Set_Phase1_Status_Success": { + "runAfter": { + "RestoreUserTest_Phase1_Create_And_Assign": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreUserTest_Analyze_Phase1_Provisioning_Results": { + "runAfter": { + "RestoreUserTest_Phase1_Create_And_Assign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreUserTest_Capture_Phase1_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreUserTest_Set_Phase1_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Create_Log: ', coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Create_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", + "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "RestoreUserTest_Set_Phase1_Output_Variables": { + "actions": { + "RestoreUserTest_Set_RestoreUserTestOutputs_Phase1": { + "type": "SetVariable", + "inputs": { + "name": "RestoreUserTestOutputs", + "value": { + "result": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "N/A", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": null + } + } + } + } + }, + "runAfter": { + "RestoreUserTest_Analyze_Phase1_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreUserTest_Check_Phase1_Status": { + "actions": { + "RestoreUserTest_Phase2_Deassign": { + "runAfter": {}, + "type": "Scope", + "actions": { + "RestoreUserTest_Unassign_AppRole": { + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/appRoleAssignedTo/', body('RestoreUserTest_Assign_AppRole')?['id'])}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_DoUntil_Wait_For_User_Deprovision_On_SCIM": { + "runAfter": { + "RestoreUserTest_Unassign_AppRole": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "actions": { + "RestoreUserTest_SCIM_Get_Deprovision": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreUserTest_Wait_Deprovision_SCIM_15s": { + "runAfter": { + "RestoreUserTest_SCIM_Get_Deprovision": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + } + }, + "RestoreUserTest_DoUntil_Verify_Deprovision_ProvisioningLog": { + "runAfter": { + "RestoreUserTest_DoUntil_Wait_For_User_Deprovision_On_SCIM": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "actions": { + "RestoreUserTest_Get_Deprovision_Log": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', variables('restoreEntraUserId'), '%27%20and%20(provisioningAction%20eq%20%27Disable%27)')}", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_Wait_Deprovision_Log_15s": { + "runAfter": { + "RestoreUserTest_Get_Deprovision_Log": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + } + } + } + }, + "RestoreUserTest_Capture_Phase2_Failed_Actions": { + "actions": { + "RestoreUserTest_Get_Phase2_Failed_Actions": { + "type": "Compose", + "inputs": "@result('RestoreUserTest_Phase2_Deassign')" + }, + "RestoreUserTest_Filter_Phase2_Failed_Actions": { + "runAfter": { + "RestoreUserTest_Get_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('RestoreUserTest_Get_Phase2_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "RestoreUserTest_Set_Phase2_Failed_Action_Name": { + "runAfter": { + "RestoreUserTest_Filter_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionName", + "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase2_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "RestoreUserTest_Set_Phase2_Failed_Action_Response": { + "runAfter": { + "RestoreUserTest_Set_Phase2_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionResponse", + "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "RestoreUserTest_Set_Phase2_Status_Failed": { + "runAfter": { + "RestoreUserTest_Set_Phase2_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "RestoreUserTest_Phase2_Deassign": [ + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreUserTest_Set_Phase2_Status_Success": { + "runAfter": { + "RestoreUserTest_Phase2_Deassign": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreUserTest_Analyze_Phase2_Provisioning_Results": { + "runAfter": { + "RestoreUserTest_Phase2_Deassign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreUserTest_Capture_Phase2_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreUserTest_Set_Phase2_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Deprovision_Log: ', coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Deprovision_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false)), 'success', 'RestoreUserTest_SCIM_Get_Deprovision: User still active on SCIM server after disable'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false)), null, concat('Action: RestoreUserTest_SCIM_Get_Deprovision - User not disabled on SCIM server. totalResults: ', string(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 'N/A')), ', active: ', string(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('RestoreUserFailedActionName')))", + "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false))), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "RestoreUserTest_Set_Phase2_Output_Variables": { + "actions": { + "RestoreUserTest_Set_RestoreUserTestOutputs_Phase2": { + "type": "SetVariable", + "inputs": { + "name": "RestoreUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Deassign Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Deassign Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "RestoreUserTest_Analyze_Phase2_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreUserTest_Check_Phase2_Status": { + "actions": { + "RestoreUserTest_Phase3_Reassign": { + "runAfter": {}, + "type": "Scope", + "actions": { + "RestoreUserTest_Reassign_AppRole": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'), '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{variables('restoreEntraUserId')}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_DoUntil_Verify_SCIM_Restore": { + "runAfter": { + "RestoreUserTest_Reassign_AppRole": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "actions": { + "RestoreUserTest_SCIM_Get_Restore": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreUserTest_Wait_Restore_30s": { + "runAfter": { + "RestoreUserTest_SCIM_Get_Restore": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + }, + "RestoreUserTest_Set_FinalScimId": { + "runAfter": { + "RestoreUserTest_DoUntil_Verify_SCIM_Restore": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "restoreFinalScimId", + "value": "@{coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['id'], '')}" + } + }, + "RestoreUserTest_Set_IdentityLink": { + "runAfter": { + "RestoreUserTest_Set_FinalScimId": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "restoreIdentityLinkPreserved", + "value": "@and(not(empty(variables('restoreFinalScimId'))), equals(variables('restoreOriginalScimId'), variables('restoreFinalScimId')))" + } + }, + "RestoreUserTest_DoUntil_Verify_Restore_ProvisioningLog": { + "runAfter": { + "RestoreUserTest_Set_IdentityLink": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@equals(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], ''), 'success')", + "limit": { + "count": 20, + "timeout": "PT10M" + }, + "actions": { + "RestoreUserTest_Get_Restore_Log": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20''', triggerBody()?['initializationData']?['syncJobId'], '''%20and%20servicePrincipal/id%20eq%20''', parameters('servicePrincipalId'), '''%20and%20sourceIdentity/id%20eq%20''', variables('restoreEntraUserId'), '''')}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreUserTest_Wait_Restore_Log_30s": { + "runAfter": { + "RestoreUserTest_Get_Restore_Log": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + } + } + }, + "RestoreUserTest_Capture_Phase3_Failed_Actions": { + "actions": { + "RestoreUserTest_Get_Phase3_Failed_Actions": { + "type": "Compose", + "inputs": "@result('RestoreUserTest_Phase3_Reassign')" + }, + "RestoreUserTest_Filter_Phase3_Failed_Actions": { + "runAfter": { + "RestoreUserTest_Get_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('RestoreUserTest_Get_Phase3_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "RestoreUserTest_Set_Phase3_Failed_Action_Name": { + "runAfter": { + "RestoreUserTest_Filter_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionName", + "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase3_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase3_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "RestoreUserTest_Set_Phase3_Failed_Action_Response": { + "runAfter": { + "RestoreUserTest_Set_Phase3_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserFailedActionResponse", + "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase3_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase3_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "RestoreUserTest_Set_Phase3_Status_Failed": { + "runAfter": { + "RestoreUserTest_Set_Phase3_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "RestoreUserTest_Phase3_Reassign": [ + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreUserTest_Set_Phase3_Status_Success": { + "runAfter": { + "RestoreUserTest_Phase3_Reassign": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreUserScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreUserTest_Analyze_Phase3_Provisioning_Results": { + "runAfter": { + "RestoreUserTest_Phase3_Reassign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreUserTest_Capture_Phase3_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreUserTest_Set_Phase3_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Restore_Log: ', coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Restore_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", + "deprovisionAction": "@coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Unknown')", + "restoreMode": "@'SoftDeleteRestore'", + "scimCheckResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), 'success', if(not(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))), 'RestoreUserTest_SCIM_Get_Restore: User not re-active on SCIM server after reassign', concat('RestoreUserTest_SCIM_Get_Restore: Identity link NOT preserved (RFC 7643 Section 3.1). Original SCIM id: ', variables('restoreOriginalScimId'), ', Final SCIM id: ', variables('restoreFinalScimId')))), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), null, if(not(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))), concat('Action: RestoreUserTest_SCIM_Get_Restore - User not restored on SCIM server. totalResults: ', string(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 'N/A')), ', active: ', string(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], 'N/A'))), concat('Action: RestoreUserTest_SCIM_Get_Restore - Identity link NOT preserved (RFC 7643 Section 3.1). Original: ', variables('restoreOriginalScimId'), ', Final: ', variables('restoreFinalScimId')))), concat('Scope execution failed. Failed action: ', variables('RestoreUserFailedActionName')))", + "identityLinkCheck": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(variables('restoreIdentityLinkPreserved'), 'PASSED', concat('FAILED: RFC 7643 Section 3.1 violation - SCIM id changed from ', variables('restoreOriginalScimId'), ' to ', variables('restoreFinalScimId'))), 'SKIPPED')", + "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), 'PASSED', 'FAILED'), 'FAILED'), 'FAILED')" + } + }, + "RestoreUserTest_Set_Phase3_Output_Variables": { + "actions": { + "RestoreUserTest_Set_RestoreUserTestOutputs_Phase3": { + "type": "SetVariable", + "inputs": { + "name": "RestoreUserTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success')), concat('success (', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['restoreMode'], 'Unknown'), ')'), if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Reassign Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Reassign Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "originalScimId": "@variables('restoreOriginalScimId')", + "finalScimId": "@variables('restoreFinalScimId')", + "identityLinkPreserved": "@variables('restoreIdentityLinkPreserved')", + "deprovisionAction": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['deprovisionAction'], 'Unknown')", + "restoreMode": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['restoreMode'], 'Unknown')", + "identityLinkCheck": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['identityLinkCheck'], 'SKIPPED')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "RestoreUserTest_Analyze_Phase3_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "RestoreUserTest_Set_Phase2_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('RestoreUserTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + } + }, + "runAfter": { + "RestoreUserTest_Set_Phase1_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('RestoreUserTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "RestoreUserTest_Cleanup_DeleteUser": { + "runAfter": { + "RestoreUserTest_Check_Phase1_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + } + } }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserFailedActionResponse", - "value": "@if(greater(length(body('DeleteUser_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('DeleteUser_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "DeleteUser_Set_Phase1_Status_Failed": { - "runAfter": { - "DeleteUser_Set_Phase1_Failed_Action_Response": [ - "Succeeded" - ] + "expression": { + "and": [ + { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Restore_User_Test" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + } + ] + } + ] }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "DeleteUser_Set_Phase1_Status_Success": { - "runAfter": { - "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "DeleteUser_Analyze_Phase1_Provisioning_Results": { - "runAfter": { - "DeleteUser_Phase1_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "DeleteUser_Capture_Phase1_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "DeleteUser_Set_Phase1_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DeleteUser_Verify_Provisioning_Logs: ', coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DeleteUser_Verify_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0), false), 'success', 'DeleteUser_Check_User_Synced: User not found on SCIM server after provisioning'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: User not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('DeleteUser_Check_User_Synced')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DeleteUserFailedActionName')))", - "overallResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "DeleteUser_Set_Phase1_Output_Variables": { - "actions": { - "DeleteUser_Set_DeleteUserTestOutputs_Phase1": { - "type": "SetVariable", - "inputs": { - "name": "DeleteUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase1_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "DeleteUser_Analyze_Phase1_Provisioning_Results": [ - "Succeeded", - "Failed" - ] + "type": "If" }, - "type": "Scope" - }, - "DeleteUser_Check_Phase1_Status": { - "actions": { - "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": { + "POD_User_Test": { "actions": { - "DeleteUser_Query_User_By_Id": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['deleteUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DeleteUser_Delete_User_From_Graph": { - "runAfter": { - "DeleteUser_Query_User_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DeleteUser_Purge_From_Recycle_Bin": { - "runAfter": { - "DeleteUser_Delay_Before_Purge": [ - "Succeeded" - ] + "POD_User_Test_Actions": { + "type": "Scope", + "actions": { + "Compose_PODUser_Body": { + "runAfter": {}, + "type": "Compose", + "inputs": "@setProperty(setProperty(setProperty(setProperty(triggerBody()?['initializationData']?['userBody'], 'userPrincipalName', triggerBody()?['initializationData']?['testUserNames']?['podUser']), 'mail', triggerBody()?['initializationData']?['testUserNames']?['podUserMail']), 'displayName', concat('PODTestUser_', triggerBody()?['initializationData']?['testUserNames']?['podUser'])), 'mailNickname', triggerBody()?['initializationData']?['testUserNames']?['podUserNickname'])" + }, + "POD_Create_Graph_User": { + "runAfter": { + "Compose_PODUser_Body": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": "@outputs('Compose_PODUser_Body')", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_SCIM_Pre_Check_User": { + "runAfter": { + "POD_Create_Graph_User": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', body('POD_Create_Graph_User')?['userPrincipalName'], '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_Delay_After_Create": { + "runAfter": { + "POD_SCIM_Pre_Check_User": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + }, + "POD_Assign_AppRole": { + "runAfter": { + "POD_Delay_After_Create": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', body('POD_Create_Graph_User')?['id'], '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('POD_Create_Graph_User')?['id']}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Delay_After_AppRole": { + "runAfter": { + "POD_Assign_AppRole": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 60, + "unit": "Second" + } + } + }, + "POD_Get_Sync_Rules": { + "runAfter": { + "POD_Delay_After_AppRole": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/synchronization/jobs/', triggerBody()?['initializationData']?['syncJobId'], '/schema')}", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Provision_On_Demand": { + "runAfter": { + "POD_Get_Sync_Rules": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/synchronization/jobs/', triggerBody()?['initializationData']?['syncJobId'], '/provisionOnDemand')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "parameters": [ + { + "ruleId": "@{first(body('POD_Get_Sync_Rules')?['synchronizationRules'])?['id']}", + "subjects": [ + { + "objectId": "@{body('POD_Create_Graph_User')?['id']}", + "objectTypeName": "User" + } + ] + } + ] + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Delay_After_Provision": { + "runAfter": { + "POD_Provision_On_Demand": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 2, + "unit": "Minute" + } + } + }, + "POD_SCIM_Get_User": { + "runAfter": { + "POD_Delay_After_Provision": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', body('POD_Create_Graph_User')?['userPrincipalName'], '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_Until_Provisioning_Logs": { + "runAfter": { + "POD_SCIM_Get_User": [ + "Succeeded", + "Failed" + ] + }, + "type": "Until", + "expression": "@greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 30, + "timeout": "PT15M" + }, + "actions": { + "POD_Verify_Provisioning_Logs": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('POD_Create_Graph_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Delay_Log_Poll": { + "runAfter": { + "POD_Verify_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + } + } }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/directory/deletedItems/@{body('DeleteUser_Create_Delete_User_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DeleteUser_DoUntil_Wait_For_User_Deletion": { - "actions": { - "DeleteUser_Check_User_Deleted": { - "type": "Http", + "POD_User_Analyze_Results": { + "runAfter": { + "POD_User_Test_Actions": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "Compose", "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('DeleteUser_Query_User_By_Id')?['Resources'])?['id'], ''))}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } + "testName": "POD_User_Test", + "timestamp": "@utcNow()", + "graphUserId": "@coalesce(body('POD_Create_Graph_User')?['id'], '')", + "graphUserUpn": "@coalesce(body('POD_Create_Graph_User')?['userPrincipalName'], '')", + "appRoleAssigned": "@equals(outputs('POD_Assign_AppRole')?['statusCode'], 201)", + "podStatusCode": "@coalesce(outputs('POD_Provision_On_Demand')?['statusCode'], 0)", + "podSucceeded": "@equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200)", + "scimGetStatusCode": "@coalesce(outputs('POD_SCIM_Get_User')?['statusCode'], 0)", + "scimUserFound": "@greater(length(coalesce(body('POD_SCIM_Get_User')?['Resources'], json('[]'))), 0)", + "scimUserId": "@coalesce(first(body('POD_SCIM_Get_User')?['Resources'])?['id'], '')", + "provisioningLogFound": "@greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningLogStatus": "@coalesce(first(body('POD_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'notFound')", + "overallResult": "@if(and(equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200), greater(length(coalesce(body('POD_SCIM_Get_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED')" } - }, - "DeleteUser_Delay_Deletion_15s": { + }, + "Set_PODUserTestOutputs": { "runAfter": { - "DeleteUser_Check_User_Deleted": [ - "Succeeded", - "Failed" - ] + "POD_User_Analyze_Results": [ + "Succeeded" + ] }, - "type": "Wait", + "type": "SetVariable", "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + "name": "PODUserTestOutputs", + "value": { + "overallResult": "@coalesce(outputs('POD_User_Analyze_Results')?['overallResult'], 'FAILED')", + "result": "@if(equals(coalesce(outputs('POD_User_Analyze_Results')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(not(equals(coalesce(outputs('POD_User_Analyze_Results')?['podStatusCode'], 0), 200)), concat('FAILED - Action: POD_Provision_On_Demand - HTTP ', string(coalesce(outputs('POD_User_Analyze_Results')?['podStatusCode'], 0))), if(equals(coalesce(outputs('POD_User_Analyze_Results')?['scimUserFound'], false), false), 'FAILED - Action: POD_SCIM_Get_User - user not found on SCIM endpoint', 'FAILED - Action: POD_Verify_Provisioning_Logs - provisioning log not found')))", + "summary": "@outputs('POD_User_Analyze_Results')", + "errorDetails": { + "podResponse": "@if(not(equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200)), string(coalesce(body('POD_Provision_On_Demand'), 'no response')), null)" + } + } } - } - }, - "runAfter": { - "DeleteUser_Purge_From_Recycle_Bin": [ - "Succeeded" - ] }, - "expression": "@equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404)", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DeleteUser_DoUntil_Poll_Delete_Provisioning_Logs": { - "actions": { - "DeleteUser_Verify_Delete_Provisioning_Logs": { + "POD_User_Cleanup_SCIM_Delete": { + "runAfter": { + "Set_PODUserTestOutputs": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, "type": "Http", "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DeleteUser_Create_Delete_User_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Delete%27&$orderby=activityDateTime%20desc&$top=1')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - }, - "runAfter": {} - }, - "DeleteUser_Delay": { + "method": "DELETE", + "uri": "@{concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('POD_SCIM_Get_User')?['Resources'])?['id'], 'not-found'))}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_User_Cleanup_Graph_Delete": { "runAfter": { - "DeleteUser_Verify_Delete_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] + "POD_User_Cleanup_SCIM_Delete": [ + "Succeeded", + "Failed", + "Skipped" + ] }, - "type": "Wait", + "type": "Http", "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', coalesce(body('POD_Create_Graph_User')?['id'], 'not-found'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } } - } - }, - "runAfter": { - "DeleteUser_DoUntil_Wait_For_User_Deletion": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - }, - "DeleteUser_Delay_Before_Purge": { - "type": "Wait", - "inputs": { - "interval": { - "count": 2, - "unit": "Minute" - } - }, - "runAfter": { - "DeleteUser_Delete_User_From_Graph": [ - "Succeeded" - ] - } - } - }, - "type": "Scope" - }, - "DeleteUser_Capture_Phase2_Failed_Actions": { - "actions": { - "DeleteUser_Get_Phase2_Failed_Actions": { - "type": "Compose", - "inputs": "@result('DeleteUser_Phase2_Verify_Deletion_And_Provisioning')" - }, - "DeleteUser_Filter_Phase2_Failed_Actions": { - "runAfter": { - "DeleteUser_Get_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('DeleteUser_Get_Phase2_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "DeleteUser_Set_Phase2_Failed_Action_Name": { - "runAfter": { - "DeleteUser_Filter_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserFailedActionName", - "value": "@{if(greater(length(body('DeleteUser_Filter_Phase2_Failed_Actions')), 0), first(body('DeleteUser_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "DeleteUser_Set_Phase2_Failed_Action_Response": { - "runAfter": { - "DeleteUser_Set_Phase2_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserFailedActionResponse", - "value": "@if(greater(length(body('DeleteUser_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('DeleteUser_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" } - }, - "DeleteUser_Set_Phase2_Status_Failed": { - "runAfter": { - "DeleteUser_Set_Phase2_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "DeleteUser_Set_Phase2_Status_Success": { - "runAfter": { - "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ - "Succeeded" - ] }, - "type": "SetVariable", - "inputs": { - "name": "DeleteUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "DeleteUser_Analyze_Phase2_Provisioning_Results": { - "runAfter": { - "DeleteUser_Phase2_Verify_Deletion_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "DeleteUser_Capture_Phase2_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "DeleteUser_Set_Phase2_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "deleteLogsCount": "@length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]')))", - "hasDeleteLogs": "@greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", - "deleteStatus": "@if(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DeleteUser_Verify_Delete_Provisioning_Logs: ', coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DeleteUser_Verify_Delete_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), not(equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404), 'success', concat('User still present on SCIM server (statusCode: ', string(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0)), ')')), variables('DeleteUserFailedActionResponse'))", - "scimCheckErrorDetails": "@if(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), if(equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404), null, concat('User still exists on SCIM server. statusCode: ', string(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0)), ', active: ', string(coalesce(body('DeleteUser_Check_User_Deleted')?['active'], 'N/A')))), concat('Scope execution status: ', variables('DeleteUserScopeExecutionStatus')))", - "overallResult": "@if(and(equals(variables('DeleteUserScopeExecutionStatus'), 'Succeeded'), and(and(greater(length(coalesce(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DeleteUser_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404))), 'PASSED', 'FAILED')" - } - }, - "DeleteUser_Set_Phase2_Output_Variables": { - "actions": { - "DeleteUser_Set_DeleteUserTestOutputs_Phase2": { - "type": "SetVariable", - "inputs": { - "name": "DeleteUserTestOutputs", - "value": { - "result": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Delete Phase] ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Delete Phase] Provisioning: ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DeleteUser_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + "runAfter": {}, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "POD_User_Test" + ] } - } - } - } - }, - "runAfter": { - "DeleteUser_Analyze_Phase2_Provisioning_Results": [ - "Succeeded", - "Failed" - ] + ] }, - "type": "Scope" - } - }, - "runAfter": { - "DeleteUser_Set_Phase1_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('DeleteUserTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - } + "type": "If" + } + }, + "runAfter": { + "Response_Immediate": [ + "Succeeded" + ], + "Initialize_PODUserTestOutputs": [ + "Succeeded" + ] }, "type": "Scope" - } }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Delete_User_Test" - ] - } - ] + "Compose_Final_Results": { + "runAfter": { + "UserTests_Scope": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "CreateUserTestOutputs": "@variables('CreateUserTestOutputs')", + "UpdateUserTestOutputs": "@variables('UpdateUserTestOutputs')", + "DeleteUserTestOutputs": "@variables('DeleteUserTestOutputs')", + "ManagerTestOutputs": "@variables('ManagerTestOutputs')", + "DisableUserTestOutputs": "@variables('DisableUserTestOutputs')", + "workflowRunId": "@workflow().run.name", + "RestoreUserTestOutputs": "@variables('RestoreUserTestOutputs')", + "PODUserTestOutputs": "@variables('PODUserTestOutputs')" } - ] }, - "type": "If" - }, - "User_Update_Manager_Test": { - "actions": { - "User_Update_Manager_Test_Actions": { - "actions": { - "Phase1_Create_Users_And_Initial_Sync": { - "actions": { - "Step1_Check_User_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } + "Initialize_CreateUserFailedActionName": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CreateUserFailedActionName", + "type": "string", + "value": "" } - }, - "Step2_Create_Test_User": { - "runAfter": { - "Step1_Check_User_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUser']}", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUser']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUser']}", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + ] + } + }, + "Initialize_CreateUserFailedActionResponse": { + "runAfter": { + "Initialize_CreateUserFailedActionName": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CreateUserFailedActionResponse", + "type": "Object", + "value": {} } - }, - "Step3_Create_Initial_Manager": { - "runAfter": { - "Step2_Create_Test_User": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "Initial Manager User", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserInitialNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial']}", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + ] + } + }, + "Initialize_CreateUserScopeExecutionStatus": { + "runAfter": { + "Initialize_CreateUserFailedActionResponse": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CreateUserScopeExecutionStatus", + "type": "string", + "value": "InProgress" } - }, - "Step4_Create_New_Manager": { - "runAfter": { - "Step3_Create_Initial_Manager": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "New Manager User", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserNewNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserNew']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['managerUserNew']}", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" + ] + } + }, + "Initialize_CreateUserTestOutputs": { + "runAfter": { + "Initialize_CreateUserScopeExecutionStatus": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CreateUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Step5_Assign_Test_User_To_App": { - "runAfter": { - "Step4_Create_New_Manager": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Step2_Create_Test_User')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } } - }, - "Step6_Assign_Initial_Manager_To_App": { - "runAfter": { - "Step5_Assign_Test_User_To_App": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Step3_Create_Initial_Manager')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Step7_Assign_New_Manager_To_App": { - "runAfter": { - "Step6_Assign_Initial_Manager_To_App": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Step4_Create_New_Manager')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DoUntil_Poll_All_Users_Created": { - "actions": { - "SCIM_Query_Test_User": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "SCIM_Query_Initial_Manager": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUserInitial'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "SCIM_Query_New_Manager": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUserNew'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_15s": { - "runAfter": { - "SCIM_Query_Test_User": [ - "Succeeded", - "Failed" - ], - "SCIM_Query_Initial_Manager": [ - "Succeeded", - "Failed" - ], - "SCIM_Query_New_Manager": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Step7_Assign_New_Manager_To_App": [ - "Succeeded" - ] - }, - "expression": "@and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DoUntil_Poll_Initial_Provisioning_Logs": { - "actions": { - "Verify_Test_User_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Verify_Initial_Manager_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step3_Create_Initial_Manager')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Verify_New_Manager_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step4_Create_New_Manager')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "ManagerTest_Delay_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Test_User_Provisioning_Logs": [ - "Succeeded", - "Failed" - ], - "Verify_Initial_Manager_Provisioning_Logs": [ - "Succeeded", - "Failed" - ], - "Verify_New_Manager_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Poll_All_Users_Created": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0))", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_Phase1_Failed_Actions": { - "actions": { - "Get_Phase1_Failed_Actions": { - "type": "Compose", - "inputs": "@result('Phase1_Create_Users_And_Initial_Sync')" - }, - "Filter_Phase1_Failed_Actions": { - "runAfter": { - "Get_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Phase1_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Phase1_Failed_Action_Name": { - "runAfter": { - "Filter_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionName", - "value": "@{if(greater(length(body('Filter_Phase1_Failed_Actions')), 0), first(body('Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Phase1_Failed_Action_Response": { - "runAfter": { - "Set_Phase1_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionResponse", - "value": "@if(greater(length(body('Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_Phase1_Status_Failed": { - "runAfter": { - "Set_Phase1_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Phase1_Create_Users_And_Initial_Sync": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_Phase1_Status_Success": { - "runAfter": { - "Phase1_Create_Users_And_Initial_Sync": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Analyze_Phase1_Provisioning_Results": { - "runAfter": { - "Phase1_Create_Users_And_Initial_Sync": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Phase1_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_Phase1_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), if(and(and(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'success', concat('Verify_Test_User_Provisioning_Logs: ', coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', Verify_Initial_Manager_Provisioning_Logs: ', coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', Verify_New_Manager_Provisioning_Logs: ', coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), concat('Verify_Test_User_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'), ', Verify_Initial_Manager_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'), ', Verify_New_Manager_Provisioning_Logs: ', if(greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), 'found', 'NO_LOGS_FOUND'))), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(or(or(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), json(concat('{\"testUser\":', string(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"initialManager\":', string(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"newManager\":', string(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('ManagerTestFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0)), 'success', concat('SCIM users not found: TestUser=', string(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]')))), ', InitialManager=', string(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]')))), ', NewManager=', string(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]')))))), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0)), null, concat('SCIM verification failed: Not all users found on SCIM server. TestUser resources: ', string(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]')))), ', InitialManager resources: ', string(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]')))), ', NewManager resources: ', string(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", - "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(and(and(greater(length(coalesce(body('Verify_Test_User_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Verify_Initial_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), greater(length(coalesce(body('Verify_New_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)), not(or(or(equals(toLower(coalesce(first(body('Verify_Test_User_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure'), equals(toLower(coalesce(first(body('Verify_Initial_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')), equals(toLower(coalesce(first(body('Verify_New_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')))), and(and(greater(length(coalesce(body('SCIM_Query_Test_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('SCIM_Query_Initial_Manager')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('SCIM_Query_New_Manager')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "Set_Phase1_Output_Variables": { - "actions": { - "Set_ManagerTestOutputs_Phase1": { - "type": "SetVariable", - "inputs": { - "name": "ManagerTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Analyze_Phase1_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase1_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Analyze_Phase1_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "Cleanup_Delete_Test_User": { - "runAfter": { - "Check_Phase3_Status": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Cleanup_Delete_Initial_Manager": { - "runAfter": { - "Cleanup_Delete_Test_User": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Cleanup_Delete_New_Manager": { - "runAfter": { - "Cleanup_Delete_Initial_Manager": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Check_Phase1_Status": { - "actions": { - "Phase2_Set_Manager": { - "actions": { - "Step1_Set_Manager_Relationship": { - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", - "method": "PUT", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Step3_Create_Initial_Manager')?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - }, - "runAfter": { - "Capture_Phase2_Start_Time": [ - "Succeeded" - ] - } - }, - "DoUntil_Wait_For_Manager_Set": { - "actions": { - "SCIM_Verify_Manager_Set": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Manager_Set_15s": { - "runAfter": { - "SCIM_Verify_Manager_Set": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Step1_Set_Manager_Relationship": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DoUntil_Poll_Set_Manager_Provisioning_Logs": { - "actions": { - "Verify_Set_Manager_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase2_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delay_Set_Manager_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Set_Manager_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Wait_For_Manager_Set": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "expression": "@and(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - }, - "Capture_Phase2_Start_Time": { - "type": "Compose", - "inputs": "@utcNow()", - "runAfter": {} - } - }, - "type": "Scope" - }, - "Capture_Phase2_Failed_Actions": { - "actions": { - "Get_Phase2_Failed_Actions": { - "type": "Compose", - "inputs": "@result('Phase2_Set_Manager')" - }, - "Filter_Phase2_Failed_Actions": { - "runAfter": { - "Get_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Phase2_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Phase2_Failed_Action_Name": { - "runAfter": { - "Filter_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionName", - "value": "@{if(greater(length(body('Filter_Phase2_Failed_Actions')), 0), first(body('Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Phase2_Failed_Action_Response": { - "runAfter": { - "Set_Phase2_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionResponse", - "value": "@{if(greater(length(body('Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" - } - }, - "Set_Phase2_Status_Failed": { - "runAfter": { - "Set_Phase2_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Phase2_Set_Manager": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_Phase2_Status_Success": { - "runAfter": { - "Phase2_Set_Manager": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Analyze_Phase2_Provisioning_Results": { - "runAfter": { - "Phase2_Set_Manager": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Phase2_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_Phase2_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "setManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", - "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", - "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), 'success', concat('Verify_Set_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), ' (expected ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ')')), 'Verify_Set_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. Actual: newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), '. Error: ', coalesce(string(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for set manager operation. Phase start: ', outputs('Capture_Phase2_Start_Time'))), variables('ManagerTestFailedActionResponse'))", - "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']))), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), 'success', concat('SCIM manager verification failed: expected manager ID ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ' in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Set')), first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'])), null, concat('SCIM verification failed: Manager not correctly set. Expected manager SCIM ID: ', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. SCIM response resources count: ', string(length(coalesce(body('SCIM_Verify_Manager_Set')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", - "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Set_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id']), 'matched', concat('Provisioning log manager mismatch: expected newValue=', coalesce(first(body('SCIM_Query_Initial_Manager')?['Resources'])?['id'], 'UNKNOWN'), ', but found newValue=', coalesce(first(body('Verify_Set_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" - } - }, - "Set_Phase2_Output_Variables": { - "actions": { - "Set_ManagerTestOutputs_Phase2": { - "type": "SetVariable", - "inputs": { - "name": "ManagerTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Set Manager Phase] ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Set Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Analyze_Phase2_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Analyze_Phase2_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Set_Phase1_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('ManagerTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Check_Phase2_Status": { - "actions": { - "Phase3_Change_Manager": { - "actions": { - "Step1_Change_Manager_Relationship": { - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", - "method": "PUT", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Step4_Create_New_Manager')?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - }, - "runAfter": { - "Capture_Phase3_Start_Time": [ - "Succeeded" - ] - } - }, - "DoUntil_Wait_For_Manager_Change": { - "actions": { - "SCIM_Verify_Manager_Change": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Manager_Change_15s": { - "runAfter": { - "SCIM_Verify_Manager_Change": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Step1_Change_Manager_Relationship": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DoUntil_Poll_Change_Manager_Provisioning_Logs": { - "actions": { - "Verify_Change_Manager_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase3_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delay_Change_Manager_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Change_Manager_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Wait_For_Manager_Change": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "expression": "@and(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - }, - "Capture_Phase3_Start_Time": { - "type": "Compose", - "inputs": "@utcNow()", - "runAfter": {} - } - }, - "type": "Scope" - }, - "Capture_Phase3_Failed_Actions": { - "actions": { - "Get_Phase3_Failed_Actions": { - "type": "Compose", - "inputs": "@result('Phase3_Change_Manager')" - }, - "Filter_Phase3_Failed_Actions": { - "runAfter": { - "Get_Phase3_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Phase3_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Phase3_Failed_Action_Name": { - "runAfter": { - "Filter_Phase3_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionName", - "value": "@{if(greater(length(body('Filter_Phase3_Failed_Actions')), 0), first(body('Filter_Phase3_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Phase3_Failed_Action_Response": { - "runAfter": { - "Set_Phase3_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionResponse", - "value": "@{if(greater(length(body('Filter_Phase3_Failed_Actions')), 0), coalesce(first(body('Filter_Phase3_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" - } - }, - "Set_Phase3_Status_Failed": { - "runAfter": { - "Set_Phase3_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Phase3_Change_Manager": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_Phase3_Status_Success": { - "runAfter": { - "Phase3_Change_Manager": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Analyze_Phase3_Provisioning_Results": { - "runAfter": { - "Phase3_Change_Manager": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Phase3_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_Phase3_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "changeManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", - "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", - "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), 'success', concat('Verify_Change_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), ' (expected ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ')')), 'Verify_Change_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. Actual: newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'UNKNOWN'), '. Error: ', coalesce(string(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for change manager operation. Phase start: ', outputs('Capture_Phase3_Start_Time'))), variables('ManagerTestFailedActionResponse'))", - "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id']))), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), 'success', concat('SCIM manager verification failed: expected new manager ID ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ' in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]'))), 0), contains(string(body('SCIM_Verify_Manager_Change')), first(body('SCIM_Query_New_Manager')?['Resources'])?['id'])), null, concat('SCIM verification failed: Manager not correctly changed. Expected new manager SCIM ID: ', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), '. SCIM response resources count: ', string(length(coalesce(body('SCIM_Verify_Manager_Change')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", - "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Change_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], first(body('SCIM_Query_New_Manager')?['Resources'])?['id']), 'matched', concat('Provisioning log manager mismatch: expected newValue=', coalesce(first(body('SCIM_Query_New_Manager')?['Resources'])?['id'], 'UNKNOWN'), ', but found newValue=', coalesce(first(body('Verify_Change_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), '. This may indicate stale provisioning logs from a previous phase.')), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" - } - }, - "Set_Phase3_Output_Variables": { - "actions": { - "Set_ManagerTestOutputs_Phase3": { - "type": "SetVariable", - "inputs": { - "name": "ManagerTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Change Manager Phase] ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Change Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Analyze_Phase3_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase3_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Analyze_Phase3_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Check_Phase1_Status": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('ManagerTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Check_Phase3_Status": { - "actions": { - "Phase4_Remove_Manager": { - "actions": { - "Step1_Remove_Manager_Relationship": { - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Step2_Create_Test_User')?['id']}/manager/$ref", - "method": "DELETE", - "headers": { - "Content-Type": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - }, - "runAfter": { - "Capture_Phase4_Start_Time": [ - "Succeeded" - ] - } - }, - "DoUntil_Wait_For_Manager_Remove": { - "actions": { - "SCIM_Verify_Manager_Remove": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['managerUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Manager_Remove_15s": { - "runAfter": { - "SCIM_Verify_Manager_Remove": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Step1_Remove_Manager_Relationship": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'])))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DoUntil_Poll_Remove_Manager_Provisioning_Logs": { - "actions": { - "Verify_Remove_Manager_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('Step2_Create_Test_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20activityDateTime%20ge%20', outputs('Capture_Phase4_Start_Time'), '%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27manager%27%20or%20p/displayName%20eq%20%27urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:manager%27)')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delay_Remove_Manager_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Remove_Manager_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Wait_For_Manager_Remove": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "expression": "@and(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - }, - "Capture_Phase4_Start_Time": { - "type": "Compose", - "inputs": "@utcNow()", - "runAfter": {} - } - }, - "type": "Scope" - }, - "Capture_Phase4_Failed_Actions": { - "actions": { - "Get_Phase4_Failed_Actions": { - "type": "Compose", - "inputs": "@result('Phase4_Remove_Manager')" - }, - "Filter_Phase4_Failed_Actions": { - "runAfter": { - "Get_Phase4_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Phase4_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Phase4_Failed_Action_Name": { - "runAfter": { - "Filter_Phase4_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionName", - "value": "@{if(greater(length(body('Filter_Phase4_Failed_Actions')), 0), first(body('Filter_Phase4_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Phase4_Failed_Action_Response": { - "runAfter": { - "Set_Phase4_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestFailedActionResponse", - "value": "@{if(greater(length(body('Filter_Phase4_Failed_Actions')), 0), coalesce(first(body('Filter_Phase4_Failed_Actions'))?['outputs'], json('{}')), json('{}'))}" - } - }, - "Set_Phase4_Status_Failed": { - "runAfter": { - "Set_Phase4_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Phase4_Remove_Manager": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_Phase4_Status_Success": { - "runAfter": { - "Phase4_Remove_Manager": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "ManagerTestScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Analyze_Phase4_Provisioning_Results": { - "runAfter": { - "Phase4_Remove_Manager": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Phase4_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_Phase4_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "removeManagerProvisioningStatus": "@if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0)", - "hasFailures": "@equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'failure')", - "provisioningResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(and(equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'))), 'success', concat('Verify_Remove_Manager_Provisioning_Logs: status=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), ', manager newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), ' (expected null)')), 'Verify_Remove_Manager_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(equals(toLower(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'), not(or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))), concat('Provisioning log issue. Status: ', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), '. Expected: newValue=null. Actual: newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'), '. Error: ', coalesce(string(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation']), 'none')), null), concat('No provisioning logs found after phase start time for remove manager operation. Phase start: ', outputs('Capture_Phase4_Start_Time'))), variables('ManagerTestFailedActionResponse'))", - "overallResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), and(equals(toLower(coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')))), and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], ''))))), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], '')))), 'success', concat('SCIM manager verification failed: manager still present in SCIM response')), concat('Failed Action: ', variables('ManagerTestFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('SCIM_Verify_Manager_Remove')?['Resources'], json('[]'))), 0), or(not(contains(string(body('SCIM_Verify_Manager_Remove')), 'manager')), empty(coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], '')))), null, concat('SCIM verification failed: Manager not correctly removed. SCIM response still contains manager value: ', coalesce(first(body('SCIM_Verify_Manager_Remove')?['Resources'])?['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']?['manager']?['value'], 'UNKNOWN'))), concat('Scope execution failed. Failed action: ', variables('ManagerTestFailedActionName')))", - "provisioningLogManagerMatch": "@if(equals(variables('ManagerTestScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Remove_Manager_Provisioning_Logs')?['value'], json('[]'))), 0), if(or(empty(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']), equals(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null')), 'matched', concat('Provisioning log manager mismatch: expected newValue=null, but found newValue=', coalesce(first(body('Verify_Remove_Manager_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'], 'null'))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('ManagerTestFailedActionName')))" - } - }, - "Set_Phase4_Output_Variables": { - "actions": { - "Set_ManagerTestOutputs_Phase4": { - "type": "SetVariable", - "inputs": { - "name": "ManagerTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Remove Manager Phase] ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Remove Manager Phase] Provisioning: ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Analyze_Phase4_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Analyze_Phase4_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Analyze_Phase4_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Check_Phase2_Status": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('ManagerTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - } - }, - "type": "Scope" - } - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isManagerAttributeSupported']", - true - ] - }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "User_Update_Manager_Test" - ] - } - ] - } - ] - }, - "type": "If" - }, - "Disable_User_Test": { - "actions": { - "DisableUserTest_Disable_User_Test_Actions": { - "actions": { - "DisableUserTest_Phase1_Create_User_And_Verify": { - "actions": { - "DisableUserTest_Disable_Step1_Check_User_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}", - "Content-Type": "@{parameters('scimContentType')}" - } - } - }, - "DisableUserTest_Disable_Step2_Create_User": { - "runAfter": { - "DisableUserTest_Disable_Step1_Check_User_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{triggerBody()?['initializationData']?['testUserNames']?['disableUser']}", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['disableUserNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['disableUser']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['disableUser']}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DisableUserTest_Disable_Assign_User_To_App": { - "runAfter": { - "DisableUserTest_Disable_Step2_Create_User": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DisableUserTest_Disable_DoUntil_Wait_For_User_Creation": { - "actions": { - "DisableUserTest_Disable_Step3_Fetch_User_By_Filter": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DisableUserTest_Disable_Delay_Creation_15s": { - "runAfter": { - "DisableUserTest_Disable_Step3_Fetch_User_By_Filter": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DisableUserTest_Disable_Assign_User_To_App": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Step3_Fetch_User_By_Filter')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DisableUserTest_Disable_Create_Poll_Provisioning_Logs": { - "actions": { - "DisableUserTest_Disable_Create_Check_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DisableUserTest_Disable_Step2_Create_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DisableUserTest_Disable_Create_Polling_Wait": { - "runAfter": { - "DisableUserTest_Disable_Create_Check_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DisableUserTest_Disable_DoUntil_Wait_For_User_Creation": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "DisableUserTest_Capture_Phase1_Failed_Actions": { - "actions": { - "DisableUserTest_Get_Phase1_Failed_Actions": { - "type": "Compose", - "inputs": "@result('DisableUserTest_Phase1_Create_User_And_Verify')" - }, - "DisableUserTest_Filter_Phase1_Failed_Actions": { - "runAfter": { - "DisableUserTest_Get_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('DisableUserTest_Get_Phase1_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "DisableUserTest_Set_Phase1_Failed_Action_Name": { - "runAfter": { - "DisableUserTest_Filter_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserFailedActionName", - "value": "@{if(greater(length(body('DisableUserTest_Filter_Phase1_Failed_Actions')), 0), first(body('DisableUserTest_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "DisableUserTest_Set_Phase1_Failed_Action_Response": { - "runAfter": { - "DisableUserTest_Set_Phase1_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserFailedActionResponse", - "value": "@if(greater(length(body('DisableUserTest_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('DisableUserTest_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "DisableUserTest_Set_Phase1_Status_Failed": { - "runAfter": { - "DisableUserTest_Set_Phase1_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "DisableUserTest_Phase1_Create_User_And_Verify": [ - "Failed" - ] - }, - "type": "Scope" - }, - "DisableUserTest_Set_Phase1_Status_Success": { - "runAfter": { - "DisableUserTest_Phase1_Create_User_And_Verify": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "DisableUserTest_Analyze_Phase1_Provisioning_Results": { - "runAfter": { - "DisableUserTest_Phase1_Create_User_And_Verify": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "DisableUserTest_Capture_Phase1_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "DisableUserTest_Set_Phase1_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DisableUserTest_Disable_Create_Check_Provisioning_Logs: ', coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DisableUserTest_Disable_Create_Check_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DisableUserFailedActionResponse'))", - "overallResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "DisableUserTest_Set_Phase1_Output_Variables": { - "actions": { - "DisableUserTest_Set_DisableUserTestOutputs_Phase1": { - "type": "SetVariable", - "inputs": { - "name": "DisableUserTestOutputs", - "value": { - "result": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', if(startsWith(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "N/A", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": null - } - } - } - } - }, - "runAfter": { - "DisableUserTest_Analyze_Phase1_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "DisableUserTest_Check_Phase1_Status": { - "actions": { - "DisableUserTest_Phase2_Disable_User_And_Verify": { - "actions": { - "DisableUserTest_Disable_Step4_Query_User_By_Id": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DisableUserTest_Disable_Step5_Disable_User": { - "runAfter": { - "DisableUserTest_Disable_Step4_Query_User_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", - "method": "PATCH", - "headers": { - "Content-Type": "application/json" - }, - "body": { "accountEnabled": false }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DisableUserTest_Disable_DoUntil_Wait_For_User_Disable": { - "actions": { - "DisableUserTest_Disable_Step6_Check_User_After_Disable": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['disableUser'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "DisableUserTest_Disable_Delay_Final_15s": { - "runAfter": { - "DisableUserTest_Disable_Step6_Check_User_After_Disable": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DisableUserTest_Disable_Step5_Disable_User": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "type": "Until" - }, - "DisableUserTest_Disable_DoUntil_Poll_Disable_Provisioning_Logs": { - "actions": { - "DisableUserTest_Disable_Verify_Disable_Provisioning_Logs": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('DisableUserTest_Disable_Step2_Create_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27', 'Disable', '%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DisableUserTest_Disable_Delay_Disable_Provisioning_Logs_15s": { - "runAfter": { - "DisableUserTest_Disable_Verify_Disable_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DisableUserTest_Disable_DoUntil_Wait_For_User_Disable": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "DisableUserTest_Capture_Phase2_Failed_Actions": { - "actions": { - "DisableUserTest_Get_Phase2_Failed_Actions": { - "type": "Compose", - "inputs": "@result('DisableUserTest_Phase2_Disable_User_And_Verify')" - }, - "DisableUserTest_Filter_Phase2_Failed_Actions": { - "runAfter": { - "DisableUserTest_Get_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('DisableUserTest_Get_Phase2_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "DisableUserTest_Set_Phase2_Failed_Action_Name": { - "runAfter": { - "DisableUserTest_Filter_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserFailedActionName", - "value": "@{if(greater(length(body('DisableUserTest_Filter_Phase2_Failed_Actions')), 0), first(body('DisableUserTest_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "DisableUserTest_Set_Phase2_Failed_Action_Response": { - "runAfter": { - "DisableUserTest_Set_Phase2_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserFailedActionResponse", - "value": "@if(greater(length(body('DisableUserTest_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('DisableUserTest_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "DisableUserTest_Set_Phase2_Status_Failed": { - "runAfter": { - "DisableUserTest_Set_Phase2_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "DisableUserTest_Phase2_Disable_User_And_Verify": [ - "Failed" - ] - }, - "type": "Scope" - }, - "DisableUserTest_Set_Phase2_Status_Success": { - "runAfter": { - "DisableUserTest_Phase2_Disable_User_And_Verify": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DisableUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "DisableUserTest_Analyze_Phase2_Provisioning_Results": { - "runAfter": { - "DisableUserTest_Phase2_Disable_User_And_Verify": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "DisableUserTest_Capture_Phase2_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "DisableUserTest_Set_Phase2_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs: ', coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'DisableUserTest_Disable_Verify_Disable_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DisableUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false)), 'success', 'DisableUserTest: User still active on SCIM server after disable'), concat('Failed Action: ', variables('DisableUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false)), null, concat('Action: DisableUserTest_Disable_Step6_Check_User_After_Disable - User not disabled on SCIM server. Resources count: ', string(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]')))), ', active: ', string(coalesce(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DisableUserFailedActionName')))", - "overallResult": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), and(greater(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), equals(first(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'])?['active'], false))), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "DisableUserTest_Set_Phase2_Output_Variables": { - "actions": { - "DisableUserTest_Set_DisableUserTestOutputs_Phase2": { - "type": "SetVariable", - "inputs": { - "name": "DisableUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Disable Phase] ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Disable Phase] Provisioning: ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('DisableUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "DisableUserTest_Analyze_Phase2_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "DisableUserTest_Set_Phase1_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ + ] + } + }, + "Initialize_UpdateUserFailedActionName": { + "runAfter": { + "Initialize_CreateUserTestOutputs": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ { - "equals": [ - "@toLower(variables('DisableUserTestOutputs')['overallResult'])", - "passed" - ] + "name": "UpdateUserFailedActionName", + "type": "string", + "value": "" } - ] - }, - "type": "If" - }, - "DisableUserTest_Cleanup_Delete_User": { - "runAfter": { - "DisableUserTest_Check_Phase1_Status": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - }, - "type": "Scope" - } - }, - "else": { - "actions": {} + ] + } }, - "expression": { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Disable_User_Test" - ] + "Initialize_UpdateUserFailedActionResponse": { + "runAfter": { + "Initialize_UpdateUserFailedActionName": [ + "Succeeded" + ] }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "UpdateUserFailedActionResponse", + "type": "Object", + "value": {} + } + ] } - ] }, - "type": "If" - }, - "Restore_User_Test": { - "actions": { - "RestoreUserTest_Actions": { - "type": "Scope", - "actions": { - "RestoreUserTest_Phase1_Create_And_Assign": { - "type": "Scope", - "actions": { - "RestoreUserTest_SCIM_Pre_Check": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreUserTest_Create_Entra_User": { - "runAfter": { - "RestoreUserTest_SCIM_Pre_Check": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "https://graph.microsoft.com/v1.0/users", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{triggerBody()?['initializationData']?['testUserNames']?['restoreUser']}", - "mailNickname": "@{triggerBody()?['initializationData']?['testUserNames']?['restoreUserNickname']}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['restoreUser']}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['restoreUserMail']}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_Set_EntraUserId": { - "runAfter": { - "RestoreUserTest_Create_Entra_User": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "restoreEntraUserId", - "value": "@{body('RestoreUserTest_Create_Entra_User')?['id']}" - } - }, - "RestoreUserTest_Assign_AppRole": { - "runAfter": { - "RestoreUserTest_Set_EntraUserId": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'), '/appRoleAssignments')}", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{variables('restoreEntraUserId')}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_DoUntil_Verify_SCIM_Create": { - "runAfter": { - "RestoreUserTest_Assign_AppRole": [ - "Succeeded" - ] - }, - "type": "Until", - "expression": "@equals(coalesce(body('RestoreUserTest_SCIM_Get_Create')?['totalResults'], 0), 1)", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "actions": { - "RestoreUserTest_SCIM_Get_Create": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreUserTest_Wait_Create_30s": { - "runAfter": { - "RestoreUserTest_SCIM_Get_Create": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } - } - } - }, - "RestoreUserTest_Set_OriginalScimId": { - "runAfter": { - "RestoreUserTest_DoUntil_Verify_SCIM_Create": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "restoreOriginalScimId", - "value": "@{coalesce(first(body('RestoreUserTest_SCIM_Get_Create')?['Resources'])?['id'], '')}" - } - }, - "RestoreUserTest_DoUntil_Verify_Create_ProvisioningLog": { - "runAfter": { - "RestoreUserTest_Set_OriginalScimId": [ - "Succeeded" - ] - }, - "type": "Until", - "expression": "@equals(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], ''), 'success')", - "limit": { - "count": 20, - "timeout": "PT10M" - }, - "actions": { - "RestoreUserTest_Get_Create_Log": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20''', triggerBody()?['initializationData']?['syncJobId'], '''%20and%20servicePrincipal/id%20eq%20''', parameters('servicePrincipalId'), '''%20and%20sourceIdentity/id%20eq%20''', variables('restoreEntraUserId'), '''%20and%20provisioningAction%20eq%20''Create''')}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_Wait_Create_Log_30s": { - "runAfter": { - "RestoreUserTest_Get_Create_Log": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } - } - } - } - } - }, - "RestoreUserTest_Capture_Phase1_Failed_Actions": { - "actions": { - "RestoreUserTest_Get_Phase1_Failed_Actions": { - "type": "Compose", - "inputs": "@result('RestoreUserTest_Phase1_Create_And_Assign')" - }, - "RestoreUserTest_Filter_Phase1_Failed_Actions": { - "runAfter": { - "RestoreUserTest_Get_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('RestoreUserTest_Get_Phase1_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "RestoreUserTest_Set_Phase1_Failed_Action_Name": { - "runAfter": { - "RestoreUserTest_Filter_Phase1_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionName", - "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase1_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "RestoreUserTest_Set_Phase1_Failed_Action_Response": { - "runAfter": { - "RestoreUserTest_Set_Phase1_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionResponse", - "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "RestoreUserTest_Set_Phase1_Status_Failed": { - "runAfter": { - "RestoreUserTest_Set_Phase1_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "RestoreUserTest_Phase1_Create_And_Assign": [ - "Failed" - ] - }, - "type": "Scope" - }, - "RestoreUserTest_Set_Phase1_Status_Success": { - "runAfter": { - "RestoreUserTest_Phase1_Create_And_Assign": [ + "Initialize_UpdateUserScopeExecutionStatus": { + "runAfter": { + "Initialize_UpdateUserFailedActionResponse": [ "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "RestoreUserTest_Analyze_Phase1_Provisioning_Results": { - "runAfter": { - "RestoreUserTest_Phase1_Create_And_Assign": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "RestoreUserTest_Capture_Phase1_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "RestoreUserTest_Set_Phase1_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Create_Log: ', coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Create_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", - "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Create_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Create_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), 'PASSED', 'FAILED'), 'FAILED')" - } - }, - "RestoreUserTest_Set_Phase1_Output_Variables": { - "actions": { - "RestoreUserTest_Set_RestoreUserTestOutputs_Phase1": { - "type": "SetVariable", - "inputs": { - "name": "RestoreUserTestOutputs", - "value": { - "result": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "N/A", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": null - } - } + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "UpdateUserScopeExecutionStatus", + "type": "string", + "value": "InProgress" } - } - }, - "runAfter": { - "RestoreUserTest_Analyze_Phase1_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "RestoreUserTest_Check_Phase1_Status": { - "actions": { - "RestoreUserTest_Phase2_Deassign": { - "runAfter": {}, - "type": "Scope", - "actions": { - "RestoreUserTest_Unassign_AppRole": { - "type": "Http", - "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/appRoleAssignedTo/', body('RestoreUserTest_Assign_AppRole')?['id'])}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_DoUntil_Wait_For_User_Deprovision_On_SCIM": { - "runAfter": { - "RestoreUserTest_Unassign_AppRole": [ - "Succeeded" - ] - }, - "type": "Until", - "expression": "@and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "actions": { - "RestoreUserTest_SCIM_Get_Deprovision": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreUserTest_Wait_Deprovision_SCIM_15s": { - "runAfter": { - "RestoreUserTest_SCIM_Get_Deprovision": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - } - }, - "RestoreUserTest_DoUntil_Verify_Deprovision_ProvisioningLog": { - "runAfter": { - "RestoreUserTest_DoUntil_Wait_For_User_Deprovision_On_SCIM": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "Until", - "expression": "@greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT15M" - }, - "actions": { - "RestoreUserTest_Get_Deprovision_Log": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', variables('restoreEntraUserId'), '%27%20and%20(provisioningAction%20eq%20%27Disable%27)')}", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_Wait_Deprovision_Log_15s": { - "runAfter": { - "RestoreUserTest_Get_Deprovision_Log": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } + ] + } + }, + "Initialize_UpdateUserTestOutputs": { + "runAfter": { + "Initialize_UpdateUserScopeExecutionStatus": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "UpdateUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} } - } } - }, - "RestoreUserTest_Capture_Phase2_Failed_Actions": { - "actions": { - "RestoreUserTest_Get_Phase2_Failed_Actions": { - "type": "Compose", - "inputs": "@result('RestoreUserTest_Phase2_Deassign')" - }, - "RestoreUserTest_Filter_Phase2_Failed_Actions": { - "runAfter": { - "RestoreUserTest_Get_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('RestoreUserTest_Get_Phase2_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "RestoreUserTest_Set_Phase2_Failed_Action_Name": { - "runAfter": { - "RestoreUserTest_Filter_Phase2_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionName", - "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase2_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "RestoreUserTest_Set_Phase2_Failed_Action_Response": { - "runAfter": { - "RestoreUserTest_Set_Phase2_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionResponse", - "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "RestoreUserTest_Set_Phase2_Status_Failed": { - "runAfter": { - "RestoreUserTest_Set_Phase2_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "RestoreUserTest_Phase2_Deassign": [ - "Failed" - ] - }, - "type": "Scope" - }, - "RestoreUserTest_Set_Phase2_Status_Success": { - "runAfter": { - "RestoreUserTest_Phase2_Deassign": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Succeeded" + ] + } + }, + "Initialize_DisableUserFailedActionName": { + "runAfter": { + "Initialize_UpdateUserTestOutputs": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DisableUserFailedActionName", + "type": "string", + "value": "" } - }, - "RestoreUserTest_Analyze_Phase2_Provisioning_Results": { - "runAfter": { - "RestoreUserTest_Phase2_Deassign": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "RestoreUserTest_Capture_Phase2_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "RestoreUserTest_Set_Phase2_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Deprovision_Log: ', coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Deprovision_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", - "scimCheckResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false)), 'success', 'RestoreUserTest_SCIM_Get_Deprovision: User still active on SCIM server after disable'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false)), null, concat('Action: RestoreUserTest_SCIM_Get_Deprovision - User not disabled on SCIM server. totalResults: ', string(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 'N/A')), ', active: ', string(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('RestoreUserFailedActionName')))", - "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), and(greater(length(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'], json('[]'))), 0), equals(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], false))), 'PASSED', 'FAILED'), 'FAILED')" + ] + } + }, + "Initialize_DisableUserFailedActionResponse": { + "runAfter": { + "Initialize_DisableUserFailedActionName": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DisableUserFailedActionResponse", + "type": "Object", + "value": {} } - }, - "RestoreUserTest_Set_Phase2_Output_Variables": { - "actions": { - "RestoreUserTest_Set_RestoreUserTestOutputs_Phase2": { - "type": "SetVariable", - "inputs": { - "name": "RestoreUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Deassign Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Deassign Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase2_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "RestoreUserTest_Analyze_Phase2_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "RestoreUserTest_Check_Phase2_Status": { - "actions": { - "RestoreUserTest_Phase3_Reassign": { - "runAfter": {}, - "type": "Scope", - "actions": { - "RestoreUserTest_Reassign_AppRole": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'), '/appRoleAssignments')}", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{variables('restoreEntraUserId')}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_DoUntil_Verify_SCIM_Restore": { - "runAfter": { - "RestoreUserTest_Reassign_AppRole": [ - "Succeeded" - ] - }, - "type": "Until", - "expression": "@and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))", - "limit": { - "count": 200, - "timeout": "PT20M" - }, - "actions": { - "RestoreUserTest_SCIM_Get_Restore": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['restoreUser'], ''), '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreUserTest_Wait_Restore_30s": { - "runAfter": { - "RestoreUserTest_SCIM_Get_Restore": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } - } - } - }, - "RestoreUserTest_Set_FinalScimId": { - "runAfter": { - "RestoreUserTest_DoUntil_Verify_SCIM_Restore": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "restoreFinalScimId", - "value": "@{coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['id'], '')}" - } - }, - "RestoreUserTest_Set_IdentityLink": { - "runAfter": { - "RestoreUserTest_Set_FinalScimId": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "restoreIdentityLinkPreserved", - "value": "@and(not(empty(variables('restoreFinalScimId'))), equals(variables('restoreOriginalScimId'), variables('restoreFinalScimId')))" - } - }, - "RestoreUserTest_DoUntil_Verify_Restore_ProvisioningLog": { - "runAfter": { - "RestoreUserTest_Set_IdentityLink": [ - "Succeeded" - ] - }, - "type": "Until", - "expression": "@equals(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], ''), 'success')", - "limit": { - "count": 20, - "timeout": "PT10M" - }, - "actions": { - "RestoreUserTest_Get_Restore_Log": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20''', triggerBody()?['initializationData']?['syncJobId'], '''%20and%20servicePrincipal/id%20eq%20''', parameters('servicePrincipalId'), '''%20and%20sourceIdentity/id%20eq%20''', variables('restoreEntraUserId'), '''')}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreUserTest_Wait_Restore_Log_30s": { - "runAfter": { - "RestoreUserTest_Get_Restore_Log": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } - } - } - } - } - }, - "RestoreUserTest_Capture_Phase3_Failed_Actions": { - "actions": { - "RestoreUserTest_Get_Phase3_Failed_Actions": { - "type": "Compose", - "inputs": "@result('RestoreUserTest_Phase3_Reassign')" - }, - "RestoreUserTest_Filter_Phase3_Failed_Actions": { - "runAfter": { - "RestoreUserTest_Get_Phase3_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('RestoreUserTest_Get_Phase3_Failed_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "RestoreUserTest_Set_Phase3_Failed_Action_Name": { - "runAfter": { - "RestoreUserTest_Filter_Phase3_Failed_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionName", - "value": "@{if(greater(length(body('RestoreUserTest_Filter_Phase3_Failed_Actions')), 0), first(body('RestoreUserTest_Filter_Phase3_Failed_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "RestoreUserTest_Set_Phase3_Failed_Action_Response": { - "runAfter": { - "RestoreUserTest_Set_Phase3_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserFailedActionResponse", - "value": "@if(greater(length(body('RestoreUserTest_Filter_Phase3_Failed_Actions')), 0), coalesce(first(body('RestoreUserTest_Filter_Phase3_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "RestoreUserTest_Set_Phase3_Status_Failed": { - "runAfter": { - "RestoreUserTest_Set_Phase3_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "RestoreUserTest_Phase3_Reassign": [ - "Failed" - ] - }, - "type": "Scope" - }, - "RestoreUserTest_Set_Phase3_Status_Success": { - "runAfter": { - "RestoreUserTest_Phase3_Reassign": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RestoreUserScopeExecutionStatus", - "value": "Succeeded" - } - }, - "RestoreUserTest_Analyze_Phase3_Provisioning_Results": { - "runAfter": { - "RestoreUserTest_Phase3_Reassign": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "RestoreUserTest_Capture_Phase3_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "RestoreUserTest_Set_Phase3_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('RestoreUserTest_Get_Restore_Log: ', coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'RestoreUserTest_Get_Restore_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), equals(toLower(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RestoreUserFailedActionResponse'))", - "deprovisionAction": "@coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Unknown')", - "restoreMode": "@'SoftDeleteRestore'", - "scimCheckResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), 'success', if(not(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))), 'RestoreUserTest_SCIM_Get_Restore: User not re-active on SCIM server after reassign', concat('RestoreUserTest_SCIM_Get_Restore: Identity link NOT preserved (RFC 7643 Section 3.1). Original SCIM id: ', variables('restoreOriginalScimId'), ', Final SCIM id: ', variables('restoreFinalScimId')))), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), null, if(not(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true))), concat('Action: RestoreUserTest_SCIM_Get_Restore - User not restored on SCIM server. totalResults: ', string(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 'N/A')), ', active: ', string(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], 'N/A'))), concat('Action: RestoreUserTest_SCIM_Get_Restore - Identity link NOT preserved (RFC 7643 Section 3.1). Original: ', variables('restoreOriginalScimId'), ', Final: ', variables('restoreFinalScimId')))), concat('Scope execution failed. Failed action: ', variables('RestoreUserFailedActionName')))", - "identityLinkCheck": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(variables('restoreIdentityLinkPreserved'), 'PASSED', concat('FAILED: RFC 7643 Section 3.1 violation - SCIM id changed from ', variables('restoreOriginalScimId'), ' to ', variables('restoreFinalScimId'))), 'SKIPPED')", - "overallResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreUserTest_Get_Restore_Log')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('RestoreUserTest_Get_Restore_Log')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), if(and(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), variables('restoreIdentityLinkPreserved')), 'PASSED', 'FAILED'), 'FAILED'), 'FAILED')" - } - }, - "RestoreUserTest_Set_Phase3_Output_Variables": { - "actions": { - "RestoreUserTest_Set_RestoreUserTestOutputs_Phase3": { - "type": "SetVariable", - "inputs": { - "name": "RestoreUserTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success')), concat('success (', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['restoreMode'], 'Unknown'), ')'), if(startsWith(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Reassign Phase] ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Reassign Phase] Provisioning: ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "originalScimId": "@variables('restoreOriginalScimId')", - "finalScimId": "@variables('restoreFinalScimId')", - "identityLinkPreserved": "@variables('restoreIdentityLinkPreserved')", - "deprovisionAction": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['deprovisionAction'], 'Unknown')", - "restoreMode": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['restoreMode'], 'Unknown')", - "identityLinkCheck": "@coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['identityLinkCheck'], 'SKIPPED')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('RestoreUserTest_Analyze_Phase3_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "RestoreUserTest_Analyze_Phase3_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "RestoreUserTest_Set_Phase2_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('RestoreUserTestOutputs')['overallResult'])", - "passed" - ] + ] + } + }, + "Initialize_DisableUserScopeExecutionStatus": { + "runAfter": { + "Initialize_DisableUserFailedActionResponse": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DisableUserScopeExecutionStatus", + "type": "string", + "value": "InProgress" + } + ] + } + }, + "Initialize_DisableUserTestOutputs": { + "runAfter": { + "Initialize_DisableUserScopeExecutionStatus": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DisableUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} } - ] - }, - "type": "If" - } - }, - "runAfter": { - "RestoreUserTest_Set_Phase1_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ + } + ] + } + }, + "Initialize_DeleteUserFailedActionName": { + "runAfter": { + "Initialize_DisableUserTestOutputs": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ { - "equals": [ - "@toLower(variables('RestoreUserTestOutputs')['overallResult'])", - "passed" - ] + "name": "DeleteUserFailedActionName", + "type": "string", + "value": "" } - ] - }, - "type": "If" - }, - "RestoreUserTest_Cleanup_DeleteUser": { - "runAfter": { - "RestoreUserTest_Check_Phase1_Status": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } + ] } - } }, - "expression": { - "and": [ - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Restore_User_Test" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - } - ] + "Initialize_DeleteUserFailedActionResponse": { + "runAfter": { + "Initialize_DeleteUserFailedActionName": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DeleteUserFailedActionResponse", + "type": "Object", + "value": {} + } + ] } - ] }, - "type": "If" - }, - "POD_User_Test": { - "actions": { - "POD_User_Test_Actions": { - "type": "Scope", - "actions": { - "POD_Create_Graph_User": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "https://graph.microsoft.com/v1.0/users", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "accountEnabled": true, - "displayName": "@{concat('PODTestUser_', substring(guid(), 0, 8))}", - "mailNickname": "@{concat('poduser', substring(guid(), 0, 8))}", - "userPrincipalName": "@{concat('poduser', substring(guid(), 0, 8), '@', last(split(coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], 'x@scimtest.onmicrosoft.com'), '@')))}", - "givenName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName']), concat('Given', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['givenName'])}", - "surname": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname']), concat('Sur', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['surname'])}", - "jobTitle": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle']), concat('Title', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['jobTitle'])}", - "department": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department']), concat('Dept', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['department'])}", - "companyName": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName']), concat('Company', substring(guid(),0,6)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['companyName'])}", - "businessPhones": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['businessPhones'], json(concat('[\"+ 1-555-', substring(guid(),0,4), '\"]')))", - "mobilePhone": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['mobilePhone'])}", - "officeLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation']), concat('Office', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['officeLocation'])}", - "preferredLanguage": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage']), 'en-US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['preferredLanguage'])}", - "employeeId": "@{substring(guid(), 0, 8)}", - "employeeType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType']), 'Employee', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeType'])}", - "streetAddress": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress']), concat('Street', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['streetAddress'])}", - "city": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city']), concat('City', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['city'])}", - "state": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state']), 'WA', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['state'])}", - "country": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['country'])}", - "postalCode": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['postalCode'])}", - "usageLocation": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation']), 'US', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['usageLocation'])}", - "mail": "@{concat('poduser', substring(guid(), 0, 8), '@', last(split(coalesce(triggerBody()?['initializationData']?['testUserNames']?['createUser'], 'x@scimtest.onmicrosoft.com'), '@')))}", - "otherMails": "@coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['otherMails'], json(concat('[\"', substring(guid(),0,8), '@example.com\"]')))", - "faxNumber": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber']), concat('+1-555-', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['faxNumber'])}", - "userType": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType']), 'Member', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['userType'])}", - "employeeOrgData": "@if(or(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'])), json(concat('{\"division\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), concat('Div', substring(guid(),0,4)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division']), '\",\"costCenter\":\"', if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), substring(guid(),0,5), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter']), '\"}')), json(concat('{\"division\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['division'], '\",\"costCenter\":\"', parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['employeeOrgData']?['costCenter'], '\"}')))", - "passwordProfile": { - "forceChangePasswordNextSignIn": "@{coalesce(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['forceChangePasswordNextSignIn'], true)}", - "password": "@{if(empty(parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password']), concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8)), parameters('defaultUserProperties')[triggerBody()?['initializationData']?['testUserNames']?['index']]['passwordProfile']['password'])}" + "Initialize_DeleteUserScopeExecutionStatus": { + "runAfter": { + "Initialize_DeleteUserFailedActionResponse": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DeleteUserScopeExecutionStatus", + "type": "string", + "value": "InProgress" } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_SCIM_Pre_Check_User": { - "runAfter": { - "POD_Create_Graph_User": [ + ] + } + }, + "Initialize_DeleteUserTestOutputs": { + "runAfter": { + "Initialize_DeleteUserScopeExecutionStatus": [ "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', body('POD_Create_Graph_User')?['userPrincipalName'], '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "POD_Delay_After_Create": { - "runAfter": { - "POD_SCIM_Pre_Check_User": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } - }, - "POD_Assign_AppRole": { - "runAfter": { - "POD_Delay_After_Create": [ + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "DeleteUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} + } + } + ] + } + }, + "Initialize_ManagerTestFailedActionName": { + "runAfter": { + "Initialize_DeleteUserTestOutputs": [ "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', body('POD_Create_Graph_User')?['id'], '/appRoleAssignments')}", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('POD_Create_Graph_User')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Delay_After_AppRole": { - "runAfter": { - "POD_Assign_AppRole": [ + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ManagerTestFailedActionName", + "type": "string", + "value": "" + } + ] + } + }, + "Initialize_ManagerTestFailedActionResponse": { + "runAfter": { + "Initialize_ManagerTestFailedActionName": [ "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 60, - "unit": "Second" - } - } - }, - "POD_Get_Sync_Rules": { - "runAfter": { - "POD_Delay_After_AppRole": [ + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ManagerTestFailedActionResponse", + "type": "Object", + "value": {} + } + ] + } + }, + "Initialize_ManagerTestScopeExecutionStatus": { + "runAfter": { + "Initialize_ManagerTestFailedActionResponse": [ "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/synchronization/jobs/', triggerBody()?['initializationData']?['syncJobId'], '/schema')}", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Provision_On_Demand": { - "runAfter": { - "POD_Get_Sync_Rules": [ + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ManagerTestScopeExecutionStatus", + "type": "string", + "value": "InProgress" + } + ] + } + }, + "Initialize_ManagerTestOutputs": { + "runAfter": { + "Initialize_ManagerTestScopeExecutionStatus": [ "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/synchronization/jobs/', triggerBody()?['initializationData']?['syncJobId'], '/provisionOnDemand')}", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "parameters": [ - { - "ruleId": "@{first(body('POD_Get_Sync_Rules')?['synchronizationRules'])?['id']}", - "subjects": [ - { - "objectId": "@{body('POD_Create_Graph_User')?['id']}", - "objectTypeName": "User" - } - ] - } - ] - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Delay_After_Provision": { - "runAfter": { - "POD_Provision_On_Demand": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 2, - "unit": "Minute" - } - } - }, - "POD_SCIM_Get_User": { - "runAfter": { - "POD_Delay_After_Provision": [ + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "ManagerTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} + } + } + ] + } + }, + "Initialize_RestoreUserFailedActionName": { + "runAfter": { + "Initialize_ManagerTestOutputs": [ "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', body('POD_Create_Graph_User')?['userPrincipalName'], '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "POD_Until_Provisioning_Logs": { - "runAfter": { - "POD_SCIM_Get_User": [ - "Succeeded", - "Failed" - ] - }, - "type": "Until", - "expression": "@greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 30, - "timeout": "PT15M" - }, - "actions": { - "POD_Verify_Provisioning_Logs": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/auditLogs/provisioning?$top=1&$orderby=activityDateTime%20desc&$filter=jobId%20eq%20%27', triggerBody()?['initializationData']?['syncJobId'], '%27%20and%20servicePrincipal/id%20eq%20%27', parameters('servicePrincipalId'), '%27%20and%20sourceIdentity/id%20eq%20%27', body('POD_Create_Graph_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "RestoreUserFailedActionName", + "type": "string", + "value": "" } - }, - "POD_Delay_Log_Poll": { - "runAfter": { - "POD_Verify_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } + ] + } + }, + "Initialize_RestoreUserFailedActionResponse": { + "runAfter": { + "Initialize_RestoreUserFailedActionName": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "RestoreUserFailedActionResponse", + "type": "Object", + "value": {} } - } - } - } + ] } - }, - "POD_User_Analyze_Results": { + }, + "Initialize_RestoreUserScopeExecutionStatus": { "runAfter": { - "POD_User_Test_Actions": [ - "Succeeded", - "Failed", - "TimedOut" - ] + "Initialize_RestoreUserFailedActionResponse": [ + "Succeeded" + ] }, - "type": "Compose", + "type": "InitializeVariable", "inputs": { - "testName": "POD_User_Test", - "timestamp": "@utcNow()", - "graphUserId": "@coalesce(body('POD_Create_Graph_User')?['id'], '')", - "graphUserUpn": "@coalesce(body('POD_Create_Graph_User')?['userPrincipalName'], '')", - "appRoleAssigned": "@equals(outputs('POD_Assign_AppRole')?['statusCode'], 201)", - "podStatusCode": "@coalesce(outputs('POD_Provision_On_Demand')?['statusCode'], 0)", - "podSucceeded": "@equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200)", - "scimGetStatusCode": "@coalesce(outputs('POD_SCIM_Get_User')?['statusCode'], 0)", - "scimUserFound": "@greater(length(coalesce(body('POD_SCIM_Get_User')?['Resources'], json('[]'))), 0)", - "scimUserId": "@coalesce(first(body('POD_SCIM_Get_User')?['Resources'])?['id'], '')", - "provisioningLogFound": "@greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningLogStatus": "@coalesce(first(body('POD_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'notFound')", - "overallResult": "@if(and(equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200), greater(length(coalesce(body('POD_SCIM_Get_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('POD_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED')" + "variables": [ + { + "name": "RestoreUserScopeExecutionStatus", + "type": "string", + "value": "InProgress" + } + ] } - }, - "Set_PODUserTestOutputs": { + }, + "Initialize_RestoreUserTestOutputs": { "runAfter": { - "POD_User_Analyze_Results": [ - "Succeeded" - ] + "Initialize_RestoreUserScopeExecutionStatus": [ + "Succeeded" + ] }, - "type": "SetVariable", + "type": "InitializeVariable", "inputs": { - "name": "PODUserTestOutputs", - "value": { - "overallResult": "@coalesce(outputs('POD_User_Analyze_Results')?['overallResult'], 'FAILED')", - "result": "@if(equals(coalesce(outputs('POD_User_Analyze_Results')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(not(equals(coalesce(outputs('POD_User_Analyze_Results')?['podStatusCode'], 0), 200)), concat('FAILED - Action: POD_Provision_On_Demand - HTTP ', string(coalesce(outputs('POD_User_Analyze_Results')?['podStatusCode'], 0))), if(equals(coalesce(outputs('POD_User_Analyze_Results')?['scimUserFound'], false), false), 'FAILED - Action: POD_SCIM_Get_User - user not found on SCIM endpoint', 'FAILED - Action: POD_Verify_Provisioning_Logs - provisioning log not found')))", - "summary": "@outputs('POD_User_Analyze_Results')", - "errorDetails": { - "podResponse": "@if(not(equals(outputs('POD_Provision_On_Demand')?['statusCode'], 200)), string(coalesce(body('POD_Provision_On_Demand'), 'no response')), null)" - } - } + "variables": [ + { + "name": "RestoreUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} + } + } + ] } - }, - "POD_User_Cleanup_SCIM_Delete": { + }, + "Initialize_restoreEntraUserId": { "runAfter": { - "Set_PODUserTestOutputs": [ - "Succeeded", - "Failed", - "Skipped" - ] + "Initialize_RestoreUserTestOutputs": [ + "Succeeded" + ] }, - "type": "Http", + "type": "InitializeVariable", "inputs": { - "method": "DELETE", - "uri": "@{concat(parameters('scimEndpoint'), '/Users/', coalesce(first(body('POD_SCIM_Get_User')?['Resources'])?['id'], 'not-found'))}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } + "variables": [ + { + "name": "restoreEntraUserId", + "type": "String", + "value": "" + } + ] } - }, - "POD_User_Cleanup_Graph_Delete": { + }, + "Initialize_restoreOriginalScimId": { "runAfter": { - "POD_User_Cleanup_SCIM_Delete": [ - "Succeeded", - "Failed", - "Skipped" - ] + "Initialize_restoreEntraUserId": [ + "Succeeded" + ] }, - "type": "Http", + "type": "InitializeVariable", "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', coalesce(body('POD_Create_Graph_User')?['id'], 'not-found'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + "variables": [ + { + "name": "restoreOriginalScimId", + "type": "String", + "value": "" + } + ] } - } }, - "runAfter": {}, - "expression": { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] + "Initialize_restoreFinalScimId": { + "runAfter": { + "Initialize_restoreOriginalScimId": [ + "Succeeded" + ] }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "restoreFinalScimId", + "type": "String", + "value": "" + } + ] + } + }, + "Initialize_All_User_Test_Variables": { + "runAfter": { + "Initialize_restoreFinalScimId": [ + "Succeeded" + ] }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "POD_User_Test" - ] + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "restoreIdentityLinkPreserved", + "type": "Boolean", + "value": false + } + ] } - ] }, - "type": "If" - } - }, - "runAfter": { - "Response_Immediate": [ - "Succeeded" - ], - "Initialize_PODUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "Scope" - }, - "Compose_Final_Results": { - "runAfter": { - "UserTests_Scope": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Compose", - "inputs": { - "CreateUserTestOutputs": "@variables('CreateUserTestOutputs')", - "UpdateUserTestOutputs": "@variables('UpdateUserTestOutputs')", - "DeleteUserTestOutputs": "@variables('DeleteUserTestOutputs')", - "ManagerTestOutputs": "@variables('ManagerTestOutputs')", - "DisableUserTestOutputs": "@variables('DisableUserTestOutputs')", - "workflowRunId": "@workflow().run.name", - "RestoreUserTestOutputs": "@variables('RestoreUserTestOutputs')", - "PODUserTestOutputs": "@variables('PODUserTestOutputs')" - } - }, - "Initialize_CreateUserFailedActionName": { - "runAfter": {}, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "CreateUserFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_CreateUserFailedActionResponse": { - "runAfter": { - "Initialize_CreateUserFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "CreateUserFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_CreateUserScopeExecutionStatus": { - "runAfter": { - "Initialize_CreateUserFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "CreateUserScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_CreateUserTestOutputs": { - "runAfter": { - "Initialize_CreateUserScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "CreateUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_UpdateUserFailedActionName": { - "runAfter": { - "Initialize_CreateUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "UpdateUserFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_UpdateUserFailedActionResponse": { - "runAfter": { - "Initialize_UpdateUserFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "UpdateUserFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_UpdateUserScopeExecutionStatus": { - "runAfter": { - "Initialize_UpdateUserFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "UpdateUserScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_UpdateUserTestOutputs": { - "runAfter": { - "Initialize_UpdateUserScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "UpdateUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_DisableUserFailedActionName": { - "runAfter": { - "Initialize_UpdateUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DisableUserFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_DisableUserFailedActionResponse": { - "runAfter": { - "Initialize_DisableUserFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DisableUserFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_DisableUserScopeExecutionStatus": { - "runAfter": { - "Initialize_DisableUserFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DisableUserScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_DisableUserTestOutputs": { - "runAfter": { - "Initialize_DisableUserScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DisableUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_DeleteUserFailedActionName": { - "runAfter": { - "Initialize_DisableUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DeleteUserFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_DeleteUserFailedActionResponse": { - "runAfter": { - "Initialize_DeleteUserFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DeleteUserFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_DeleteUserScopeExecutionStatus": { - "runAfter": { - "Initialize_DeleteUserFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DeleteUserScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_DeleteUserTestOutputs": { - "runAfter": { - "Initialize_DeleteUserScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "DeleteUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_ManagerTestFailedActionName": { - "runAfter": { - "Initialize_DeleteUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "ManagerTestFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_ManagerTestFailedActionResponse": { - "runAfter": { - "Initialize_ManagerTestFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "ManagerTestFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_ManagerTestScopeExecutionStatus": { - "runAfter": { - "Initialize_ManagerTestFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "ManagerTestScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_ManagerTestOutputs": { - "runAfter": { - "Initialize_ManagerTestScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "ManagerTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_RestoreUserFailedActionName": { - "runAfter": { - "Initialize_ManagerTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "RestoreUserFailedActionName", - "type": "string", - "value": "" - } - ] - } - }, - "Initialize_RestoreUserFailedActionResponse": { - "runAfter": { - "Initialize_RestoreUserFailedActionName": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "RestoreUserFailedActionResponse", - "type": "Object", - "value": {} - } - ] - } - }, - "Initialize_RestoreUserScopeExecutionStatus": { - "runAfter": { - "Initialize_RestoreUserFailedActionResponse": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "RestoreUserScopeExecutionStatus", - "type": "string", - "value": "InProgress" - } - ] - } - }, - "Initialize_RestoreUserTestOutputs": { - "runAfter": { - "Initialize_RestoreUserScopeExecutionStatus": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "RestoreUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - }, - "Initialize_restoreEntraUserId": { - "runAfter": { - "Initialize_RestoreUserTestOutputs": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "restoreEntraUserId", - "type": "String", - "value": "" - } - ] - } - }, - "Initialize_restoreOriginalScimId": { - "runAfter": { - "Initialize_restoreEntraUserId": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "restoreOriginalScimId", - "type": "String", - "value": "" - } - ] - } - }, - "Initialize_restoreFinalScimId": { - "runAfter": { - "Initialize_restoreOriginalScimId": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "restoreFinalScimId", - "type": "String", - "value": "" - } - ] - } - }, - "Initialize_All_User_Test_Variables": { - "runAfter": { - "Initialize_restoreFinalScimId": [ - "Succeeded" - ] - }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "restoreIdentityLinkPreserved", - "type": "Boolean", - "value": false + "Initialize_PODUserTestOutputs": { + "runAfter": { + "Initialize_All_User_Test_Variables": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "PODUserTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} + } + } + ] + } } - ] - } - }, - "Initialize_PODUserTestOutputs": { - "runAfter": { - "Initialize_All_User_Test_Variables": [ - "Succeeded" - ] }, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "PODUserTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] - } - } + "outputs": {} }, - "outputs": {} - }, - "kind": "Stateful" + "kind": "Stateful" } \ No newline at end of file diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION index dd98ee6c..2f52450b 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION @@ -1 +1 @@ -9.0 +10.0 diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/scim-onboarding.agent.md b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/scim-onboarding.agent.md index fea0b33c..da369bf1 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/scim-onboarding.agent.md +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/scim-onboarding.agent.md @@ -41,7 +41,7 @@ Collect the ISV's SCIM endpoint and bearer token, validate their Azure environme 📄 **https://forms.microsoft.com/pages/responsepage.aspx?id=v4j5cvGGr0GRqy180BHbR3elR4YvzS1IhaP_XITThvJUODY1UTJSSUFXTzFYMTQ0SkxSWTY4OTYzRi4u&route=shorturl** - After submitting, the ISV must **wait for an explicit confirmation from the Microsoft team** that their tenant has been whitelisted. This typically takes **2 business days**. Do not attempt to continue with the onboarding steps until this confirmation is received — any Azure or Entra resource creation will fail if the tenant has not yet been approved. + After submitting, the ISV must **wait for an explicit confirmation from the Microsoft team** that their tenant has been whitelisted. Allowlisting will be available **within 2 business days**. Do not attempt to continue with the onboarding steps until this confirmation is received — any Azure or Entra resource creation will fail if the tenant has not yet been approved. Use `ask_user` to confirm the ISV has received whitelisting confirmation from Microsoft before proceeding. Do NOT continue to Step 2 until confirmed. @@ -146,6 +146,10 @@ Present a summary to the ISV: SCIM capabilities: Users ✓ Groups <✓|✗> Manager <✓|✗> Soft delete <✓|✗> ``` +If `/Groups` is unsupported, warn the ISV: + +> ⚠️ **Your SCIM endpoint does not support `/Groups`.** The validation suite includes Group tests (`Create_Group_Test`, `Update_Group_Test`, `Delete_Group_Test`, `SCIM_Group_Create_Test`, `SCIM_Group_Update_Test`, `SCIM_Group_Pagination_Test`, `POD_Group_Test`, `Restore_Group_Test`) that **will fail** because they call `/Groups` on your endpoint. These failures will block submission — the test suite requires all executed tests to pass. To pass Group tests, your SCIM server must implement the `/Groups` resource type and return HTTP 200 for `GET /Groups` (even if the response is an empty list). If you choose to proceed without Group support, the agent will apply Pattern #15 (disable Group sync mappings) to prevent Entra quarantine, but the Logic App Group tests will still fail and submission will not be possible until Groups are implemented. + If empty filter is non-compliant, **STOP** and tell the ISV this is a mandatory requirement. They must fix their SCIM server before proceeding. ### Failure handling @@ -179,7 +183,7 @@ az rest --method POST \ > > 📄 **https://forms.microsoft.com/pages/responsepage.aspx?id=v4j5cvGGr0GRqy180BHbR3elR4YvzS1IhaP_XITThvJUODY1UTJSSUFXTzFYMTQ0SkxSWTY4OTYzRi4u&route=shorturl** > -> *After submitting, wait for an explicit confirmation from the Microsoft team that your tenant has been whitelisted (typically 2 business days). Do not attempt to continue until you receive this confirmation. Once confirmed, restart the agent from the beginning.* +> *After submitting, wait for an explicit confirmation from the Microsoft team that your tenant has been whitelisted. Allowlisting will be available within 2 business days. Do not attempt to continue until you receive this confirmation. Once confirmed, restart the agent from the beginning.* **If the instantiate call succeeds (HTTP 200/201):** @@ -234,6 +238,8 @@ az rest --method POST \ | HTTP 400 `RequestMissingRequiredParameter` | Body is missing `templateId` or `credentials` array | Fix the body shape. | | Any other 4xx/5xx | Unexpected | Surface verbatim and abort. | +Before aborting on any validation failure, capture the exact inner error and match it against **Step 6d: Known Issues**. In particular, `SystemForCrossDomainIdentityManagementServiceIncompatible`, `CredentialValidationUnavailable`, or an inner Group `404` may match **Pattern #15**. At this pre-job stage there is no job schema to update, so do not bypass the creation gate or invent a schema target; report the known issue as a possible cause and apply Pattern #15 only if a synchronization job already exists. + **Sub-step 2b-2: PATCH the credentials into `/synchronization/connectivityParameters` (portal path)** > **IMPORTANT (beta portal path):** Save sync connectivity using `PATCH https://graph.microsoft.com/beta/servicePrincipals//synchronization/connectivityParameters` with the lower-case key names shown below. This is the portal-style path and avoids the `Oauth2TokenExchangeUri ... NotInAllowList` failure seen on `PUT /v1.0/.../synchronization/secrets` for OAuth. @@ -294,6 +300,8 @@ az rest --method POST \ | HTTP 500 `InternalError` — `"Requested value 'X' was not found"` | Inline validation payload included an unsupported key (for example `CredentialLocationInRequest`) or the connectivity parameters used the wrong key casing | Re-run inline validation with only the supported OAuth keys, then re-PATCH `connectivityParameters` using the lower-case portal keys (`authenticationType`, `baseAddress`, `oauth2ClientId`, `oauth2ClientSecret`, `oauth2TokenExchangeUri`, `credentialLocationInRequest`). Then re-validate. | | Any other 4xx/5xx | Unexpected | Surface verbatim and **ABORT**. | +Before aborting or retrying, capture the exact inner error and match it against **Step 6d: Known Issues**. If the response mentions `SystemForCrossDomainIdentityManagementServiceIncompatible`, Group connectivity, or an inner Group `404`, evaluate **Pattern #15**. Do not classify a generic `401`, `403`, rejected bearer token, or OAuth token-exchange failure as Pattern #15. + > ⚠️ **GATE: Do NOT proceed to Sub-step 2b-3 until the saved-credential Test Connection returns 200/204.** Creating a sync job with invalid credentials causes immediate quarantine, and recovering from quarantine requires a full restart cycle (re-PATCH connectivity parameters → restart job with `resetScope: Full` → start → re-verify). It is far cheaper to fix credentials now. **Sub-step 2b-3: Create the sync job** @@ -327,7 +335,7 @@ else: > > 📄 **https://forms.microsoft.com/pages/responsepage.aspx?id=v4j5cvGGr0GRqy180BHbR3elR4YvzS1IhaP_XITThvJUODY1UTJSSUFXTzFYMTQ0SkxSWTY4OTYzRi4u&route=shorturl** > -> *After submitting, wait for an explicit confirmation from the Microsoft team that your tenant has been whitelisted (typically 2 business days). Do not attempt to continue until you receive this confirmation. The Entra app and sync job have been left in place. Once confirmed, restart the agent — it will detect the existing resources and continue from where it left off.* +> *After submitting, wait for an explicit confirmation from the Microsoft team that your tenant has been whitelisted. Allowlisting will be available within 2 business days. Do not attempt to continue until you receive this confirmation. The Entra app and sync job have been left in place. Once confirmed, restart the agent — it will detect the existing resources and continue from where it left off.* **If the ISV's tenant IS in the whitelist** → proceed normally. @@ -473,7 +481,7 @@ else: print('All parameter types OK') " -# 3. BOM check — UTF-8 BOM causes runtime crash (see Pattern #15) +# 3. BOM check — UTF-8 BOM causes runtime crash (see this Step 2f validation) python -c " import glob for f in glob.glob('*_Workflow.json') + ['parameters.json']: @@ -813,6 +821,8 @@ If `status.code == Quarantine` with `lastExecError == SystemForCrossDomainIdenti 3. `POST /jobs//start` again. 4. Re-run this Step 3h check. If still quarantined, abort and report the exact error. +If `status.code == Quarantine` and the inner error contains `SystemForCrossDomainIdentityManagementServiceIncompatible`, Group connectivity, or an HTTP `404`, match the exact error against **Pattern #15** before treating it as invalid credentials. Apply Pattern #15 only when Phase 1 showed that `/Groups` is unsupported and the current job schema has an enabled Group object mapping. + If `status.code` is healthy → continue to Phase 4. --- @@ -1029,7 +1039,7 @@ for i in {1..36}; do done ``` -**Pass criteria:** `state == "Running"`. If the runtime is still not `Running` after 3 minutes, abort and investigate (commonly a `workflow.json` syntax error or UTF-8 BOM corruption left the runtime in `Error` state — see Pattern #15 in Step 6d). +**Pass criteria:** `state == "Running"`. If the runtime is still not `Running` after 3 minutes, abort and investigate (commonly a `workflow.json` syntax error or UTF-8 BOM corruption left the runtime in `Error` state — see the BOM validation in Step 2f). **Optional sanity check (recommended on re-deploys):** Before triggering, GET each modified workflow and assert `health.state == "Healthy"`: ```bash @@ -1322,6 +1332,36 @@ All 8 required permissions must be present: | 12 | `NO_LOGS` / `PROVISIONING_LOGS_MISSING` **after Step 6c confirms no permission error AND Pattern #14 is ruled out** | Entra sync cycle too slow | ⚠️ Maybe | Re-run once (sync service gets faster on subsequent cycles). If same failure repeats, escalate. | | 13 | `Authentication_MSGraphPermissionMissing` | MI missing Graph permission | ✅ Yes | Parse the missing permission name(s) from the error, find the appRoleId from the Graph SP, assign via `appRoleAssignments`. This is NOT a propagation delay — the permission was never assigned. **After assigning, you MUST `az webapp restart` the LA** to force a new MI token that carries the added appRole; without restart the cached token still lacks the permission and the next run will fail identically. Poll `/host/default/properties/status` until `state=Running` (~60s), then re-run. | | 14 | `NO_LOGS_FOUND` on **every** UserTests/GroupTests test AND/OR `POD_User_Test`/`POD_Group_Test` returns 401 from `provisionOnDemand` AND/OR `GET /synchronization/jobs/.status.code == Quarantine` | Entra sync job quarantined (connectivity parameters missing/rejected) OR LA MI is not an owner of the App + SP (synchronization owner required for `provisionOnDemand`) | ✅ Yes | **Before any re-run of the orchestrator**, GET `/synchronization/jobs/` and check `status.code`. If `Quarantine`: re-PATCH `https://graph.microsoft.com/beta/servicePrincipals//synchronization/connectivityParameters` with only the supported keys for the auth mode (bearer: `authenticationType` + `baseAddress` + `secretToken`; OAuth: `authenticationType` + `baseAddress` + `oauth2ClientId` + `oauth2ClientSecret` + `oauth2TokenExchangeUri` + `credentialLocationInRequest`). Then `POST /jobs//restart` with `{"criteria":{"resetScope":"Full"}}`, then `POST /jobs//start`, then re-verify `status.code` is `Active` and `lastExecution.error` is `null`. If `provisionOnDemand` still 401s after the job is healthy: add the LA MI's enterprise object id as owner of BOTH the application and the SP — `POST /applications//owners/$ref` and `POST /servicePrincipals//owners/$ref` with body `{"@odata.id":"https://graph.microsoft.com/v1.0/directoryObjects/"}` — then `az webapp restart` the LA and re-trigger the orchestrator. | +| 15 | Entra `/validateConnectivity` behavior (surfaced through Graph `validateCredentials`) returns `SystemForCrossDomainIdentityManagementServiceIncompatible`, `CredentialValidationUnavailable`, or inner HTTP `404` involving Group connectivity **and** Phase 1 found `/Groups` unsupported **and** the job schema has an enabled Group object mapping | Entra processes or probes Group because the synchronization schema enables its Group object mapping, but the SCIM endpoint is user-only | ✅ Yes | Follow the mandatory Pattern #15 procedure below: disable every Group object mapping with a full-schema `PUT`, verify, restart/start the job, and revalidate. | +| 16 | Any Group test (`Create_Group_Test`, `Update_Group_Test`, `Delete_Group_Test`, `Group_Update_Add_Member_Test`, `Group_Update_Remove_Member_Test`, `SCIM_Group_Create_Test`, `SCIM_Group_Update_Test`, `SCIM_Group_Pagination_Test`, `POD_Group_Test`, `Restore_Group_Test`) fails with `404`, `Not Found`, or SCIM error **and** Phase 1 found `/Groups` unsupported | The Logic App test workflows call `/Groups` directly using the bearer token — Pattern #15 only disables the Entra sync-engine Group mapping, not the LA tests themselves | ❌ No | **Do NOT auto-fix or suppress.** Report to the ISV: *"Your SCIM endpoint does not implement the `/Groups` resource type. The following Group tests failed because your server returned 404 for `/Groups` requests: [list failed tests]. To pass validation and submit to the Entra app gallery, your SCIM server must support the `/Groups` endpoint and return HTTP 200 (even for an empty group list). These failures block submission."* Do NOT allow submission. Do NOT re-run — the same tests will fail identically. Wait for the ISV to confirm they have added `/Groups` support, then re-probe `/Groups` (Phase 1 Step 6) to confirm before re-triggering. | + +#### Pattern #15: Disable Group object mappings for a user-only endpoint + +This issue is commonly described as `/validateConnectivity` failing. The public Graph calls in this agent use the `validateCredentials` endpoints; the Group probe is part of Entra's underlying connectivity-validation behavior. + +Apply this recovery only after all three conditions are confirmed: +1. Phase 1 showed that `/Groups?count=1` is unsupported or returns `404`. +2. The actual inner connectivity error is `SystemForCrossDomainIdentityManagementServiceIncompatible`, `CredentialValidationUnavailable`, or an HTTP `404` involving Group. A generic `401`, `403`, rejected bearer token, or OAuth token-exchange failure does not match. +3. `GET /servicePrincipals//synchronization/jobs//schema` contains at least one object mapping where `sourceObjectName == "Group"` or `targetObjectName == "Group"` and `enabled == true`. + +If no Group mapping exists or every Group mapping is already disabled, do not write the schema. Pattern #15 is not the root cause; continue diagnosis using the exact inner error. + +When all conditions match: +1. GET the **complete** schema from `https://graph.microsoft.com/v1.0/servicePrincipals//synchronization/jobs//schema`. +2. Log every matching Group mapping's `name`, `sourceObjectName`, `targetObjectName`, and current `enabled` value. +3. Across **every** `synchronizationRules[*].objectMappings[*]`, set only `enabled = false` where `sourceObjectName == "Group"` or `targetObjectName == "Group"`. Do not assume rule index `0`, remove mappings, disable User mappings, or modify attributes/metadata. +4. Save the complete modified schema as UTF-8 **without BOM** and replace the job schema with: + ```bash + az rest --method PUT \ + --url "https://graph.microsoft.com/v1.0/servicePrincipals//synchronization/jobs//schema" \ + --headers "Content-Type=application/json" \ + --body '@updated-sync-schema.json' + ``` + This API fully replaces the schema. Do not use `PATCH` or send only the changed object mapping. Require HTTP `204 No Content`. +5. GET the complete schema again. Assert every Group object mapping is disabled and all previously enabled User object mappings remain enabled. Log the before/after Group mapping values. +6. `POST /servicePrincipals//synchronization/jobs//restart` with `{"criteria":{"resetScope":"Full"}}`, then `POST /jobs//start`. +7. Re-run saved-credential connectivity validation where applicable, then poll the job until `status.code` is `Active` or `InProgress`, `status.quarantine` is `null`, and `status.lastExecution.error` is `null` (or `lastExecution.state == "Succeeded"`). +8. Before re-triggering tests, follow the existing Step 5a sync-job health gate and mandatory Step 5a2 Logic App restart. Do not ask the ISV for permission; this pattern is auto-fixable. ### Step 6e: Extract canonical values from schema validation errors @@ -1352,9 +1392,16 @@ while (test run fails): if auto-fixable → apply fix if maybe-fixable AND root cause confirmed as timing → no fix needed if ISV-must-fix → report to ISV, wait for confirmation + if Pattern #15 → GET the full schema, disable every enabled Group + object mapping, PUT the full schema, re-GET and verify, then + restart/start/revalidate the sync job exactly as Pattern #15 requires + if Pattern #16 (Group tests failing, /Groups unsupported) → + report ALL failed Group tests to the ISV with the Pattern #16 message. + Do NOT re-run — the same tests will fail identically. + Do NOT allow submission. Wait for ISV to add /Groups support. # Mandatory pre-trigger restart on EVERY iteration (see Step 5a2): if any workflow.json was modified OR any appRoleAssignment / owner / secret - / sync-job state was changed during this iteration: + / sync schema / sync-job state was changed during this iteration: az webapp restart -n -g poll /host/default/properties/status until state == "Running" (~60s) re-trigger orchestrator (Step 5b) and watch (Step 5b loop) @@ -1370,15 +1417,64 @@ while (test run fails): ## Submit Test Results (Official Guide) ### Goal -Hand off the ISV to the official SCIMReferenceCode submission guidance instead of performing in-agent report generation and submission. +Give the ISV a complete, copy-ready submission summary for the exact successful run, then hand off to the official SCIMReferenceCode submission guidance. The agent does not submit the form on the ISV's behalf. ### Steps -1. Confirm the latest orchestrator run is successful (`FINAL STATUS: Succeeded`) and no unresolved Phase 6 issues remain. -2. Inform the ISV directly to follow the official **Submit Test Results** guidance in: - https://github.com/AzureAD/SCIMReferenceCode/blob/master/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SetupLogicApp-Standard-Agent.md#submittest-results -3. Inform the ISV directly to complete submission exactly as documented there, including required artifacts and contact path. -4. If the ISV needs help locating generated files in the current workspace, assist with file discovery only. +1. **Apply the success gate.** Continue only when all of the following are true: + - The exact captured orchestrator `` reached terminal status `Succeeded`. + - `Final_TestResults` for that same run has no blocking failures. + - No Phase 6 issue remains unresolved. + + Do not print submission details for a failed, cancelled, timed-out, running, or stale run. A workflow status of `Succeeded` and the test-result totals are separate facts; verify and print both. + +2. **Recover the service principal ID from the successful run — never from setup memory or the current parameters file.** + - GET the `Call_Initialization_Workflow` action for the exact successful ``: + `https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//hostruntime/runtime/webhooks/workflow/api/management/workflows/Orchestrator_Workflow/runs//actions/Call_Initialization_Workflow?api-version=2022-03-01` + - Follow `properties.inputsLink.uri` and read `body.servicePrincipalId` from the parent-to-child request payload. + - Reject a missing or non-GUID value. Call this value ``. + +3. **Resolve the corresponding application ID and display name from Graph.** Use the run-derived ID: + `GET https://graph.microsoft.com/v1.0/servicePrincipals/?$select=id,appId,displayName` + + Require the returned `id` to equal ``. Use the returned `appId` as `` and `displayName` as ``. Do not use the Logic App managed identity object ID. + +4. **Build both direct links.** URL-encode all substituted path values. + - Azure run link: + `https://portal.azure.com/#view/Microsoft_Azure_EMA/DesignerEditor.ReactView/id/%2Fsubscriptions%2F%2FresourceGroups%2F%2Fproviders%2FMicrosoft.Web%2Fsites%2F%2Fworkflows%2FOrchestrator_Workflow/location//isReadOnly~/false/isMonitoringView~/true/runId/` + - App-specific Entra developer portal submission link: + `https://entra.microsoft.com/?Microsoft_AAD_Connect_Provisioning=tip&feature.enableSelfServiceOnboardingDeveloperPortal=true&feature.consoletelemetry=true#view/Microsoft_AAD_Connect_Provisioning/ProvisioningMenuBlade/~/SubmitValidationResults/objectId//appId/` + - Generic developer portal fallback: + `https://entra.microsoft.com/?Microsoft_AAD_Connect_Provisioning=tip&feature.enableSelfServiceOnboardingDeveloperPortal=true&feature.consoletelemetry=true#view/Microsoft_AAD_IAM/StartboardApplicationsMenuBlade/~/AppAppsPreview` + +5. **Print this mandatory final submission summary.** Replace every placeholder with the verified value. `Run status: Succeeded` must appear literally; phrases such as "validation passed" or `0 failed` are not substitutes. + +```text +VALIDATION COMPLETE — READY TO SUBMIT + +Subscription ID: +Resource group: +Logic App name: +Workflow: Orchestrator_Workflow +Run ID: +Run status: Succeeded +Tests passed: +Tests skipped: +Tests failed: 0 + +Entra application: +Service principal object ID: +Application (client) ID: +Developer portal submission link: + +Azure run link: +Generic developer portal fallback: +Official submission guide: https://github.com/AzureAD/SCIMReferenceCode/blob/master/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SetupLogicApp-Standard-Agent.md#submittest-results + +Submission request ID: Enter the request ID supplied separately by the Microsoft team; the Logic App does not generate it. +``` + +6. Tell the ISV to review the application details and attestations before submitting because the form cannot be edited after submission. If the app-specific link does not open, use the generic fallback, select the enterprise application matching both `` and ``, then go to **Provisioning > Submit validation results**. --- @@ -1423,3 +1519,5 @@ Ask the ISV if they want to keep or delete the Logic App and resource group. 14. **Prefer `Invoke-RestMethod` over `az rest` for repeated API calls.** On Windows, each `az rest` invocation spawns a full Python process (~30-60s on machines with 32-bit Python). For any loop or sequence of 3+ API calls, fetch a token once with `az account get-access-token --query accessToken -o tsv` and use `Invoke-RestMethod -Headers @{Authorization="Bearer $token"}` for subsequent calls. Refresh the token every 25 minutes. This applies to permission assignment loops, polling loops, and diagnostic queries. 15. **Always query actual appRoleIds from the Graph service principal** — do NOT hardcode appRoleIds. They can differ across tenants/environments. Query `GET /servicePrincipals?$filter=appId eq '00000003-0000-0000-c000-000000000000'` and look up `.appRoles[]` by `.value` (permission name). Then use the `.id` field for assignment. 16. **Pre-deploy validation is mandatory** — before any VFS upload or zip deploy, run JSON syntax validation, parameter type/value checks, and BOM detection on ALL workflow files and parameters.json (see Step 2f). Deploying invalid files causes the runtime to reject ALL workflows with a generic `WorkflowNotFound` error that is only visible in Kudu host logs. +17. **A successful run requires the complete submission handoff.** After the success gate passes, do not end with only "validation passed," test totals, or a run ID. Print the mandatory `VALIDATION COMPLETE — READY TO SUBMIT` block from **Submit Test Results**, including the literal `Run status: Succeeded`, run-derived service principal ID, Graph-resolved application ID, Azure run link, and app-specific Entra developer portal link. +18. **Every failure must be checked against known issues before aborting, retrying, or escalating.** Capture the exact inner HTTP/Graph/SCIM error, then match Step 6d in every phase, including Phase 2 connectivity setup and Phase 3 sync-job health. For Phase 5/6 test failures, first complete the mandatory Step 6c child-action drill-down. Never apply a pattern from a high-level summary alone.