diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json index 14803d6..e3ac86f 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/GroupTests_Workflow.json @@ -1,3953 +1,4726 @@ { - "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", + "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": { + "initializationData": { + "type": "object", + "properties": { + "syncJobId": { + "type": "string" + }, + "testUserNames": { + "type": "object" + }, + "testGroupNames": { + "type": "object" + }, + "appRoles": { + "type": "object" + }, + "userAppRole": { + "type": "array" + }, + "schema": { + "type": "object" + } + } + }, + "EnabledTests": { + "type": "string" + }, + "parentRunId": { + "type": "string" + } + } + } + } + } + }, + "actions": { + "Response_Immediate": { + "runAfter": { + "Initialize_All_Group_Test_Variables": [ + "Succeeded" + ] + }, + "type": "Response", + "kind": "Http", + "inputs": { + "statusCode": 202, + "body": { + "workflowRunId": "@workflow().run.name", + "workflowName": "@workflow().name", + "status": "Accepted", + "message": "GroupTests workflow started. Tests are running asynchronously." + } + } + }, + "Initialize_All_Group_Test_Variables": { + "runAfter": {}, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CreateGroupFailedActionName", + "type": "string", + "value": "" + }, + { + "name": "CreateGroupFailedActionResponse", + "type": "Object", + "value": {} + }, + { + "name": "CreateGroupScopeExecutionStatus", + "type": "string", + "value": "InProgress" + }, + { + "name": "CreateGroupTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "provisioningLogsResult": "", + "scimCheckResult": "", + "errorDetails": { + "provisioningLogs": "", + "scimCheck": "" + } + } + }, + { + "name": "UpdateGroupFailedActionName", + "type": "string", + "value": "" + }, + { + "name": "UpdateGroupFailedActionResponse", + "type": "Object", + "value": {} + }, + { + "name": "UpdateGroupScopeExecutionStatus", + "type": "string", + "value": "InProgress" + }, + { + "name": "UpdateGroupTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "provisioningLogsResult": "", + "scimCheckResult": "", + "errorDetails": { + "provisioningLogs": "", + "scimCheck": "" + } + } + }, + { + "name": "DeleteGroupFailedActionName", + "type": "string", + "value": "" + }, + { + "name": "DeleteGroupFailedActionResponse", + "type": "Object", + "value": {} + }, + { + "name": "DeleteGroupScopeExecutionStatus", + "type": "string", + "value": "InProgress" + }, + { + "name": "DeleteGroupTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "provisioningLogsResult": "", + "scimCheckResult": "", + "errorDetails": { + "provisioningLogs": "", + "scimCheck": "" + } + } + }, + { + "name": "AddMemberFailedActionName", + "type": "string", + "value": "" + }, + { + "name": "AddMemberFailedActionResponse", + "type": "Object", + "value": {} + }, + { + "name": "AddMemberScopeExecutionStatus", + "type": "string", + "value": "InProgress" + }, + { + "name": "AddMemberTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "provisioningLogsResult": "", + "scimCheckResult": "", + "errorDetails": { + "provisioningLogs": "", + "scimCheck": "" + } + } + }, + { + "name": "RemoveMemberFailedActionName", + "type": "string", + "value": "" + }, + { + "name": "RemoveMemberFailedActionResponse", + "type": "Object", + "value": {} + }, + { + "name": "RemoveMemberScopeExecutionStatus", + "type": "string", + "value": "InProgress" + }, + { + "name": "RemoveMemberTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "provisioningLogsResult": "", + "scimCheckResult": "", + "errorDetails": { + "provisioningLogs": "", + "scimCheck": "" + } + } + } + ] + } + }, + "Initialize_PODGroupTestOutputs": { + "runAfter": { + "Initialize_All_Group_Test_Variables": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "PODGroupTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "errorDetails": {} + } + } + ] + } + }, + "Initialize_RestoreGroupTestVariables": { + "runAfter": { + "Initialize_PODGroupTestOutputs": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "restoreEntraGroupId", + "type": "string", + "value": "" + }, + { + "name": "restoreGroupAppRoleAssignmentId", + "type": "string", + "value": "" + }, + { + "name": "RestoreGroupTestOutputs", + "type": "Object", + "value": { + "overallResult": "SKIPPED", + "result": "SKIPPED", + "summary": null, + "detailedResults": [], + "errorDetails": { + "summary": null + } + } + } + ] + } + }, + "Initialize_RestoreGroupFailedActionName": { + "runAfter": { + "Initialize_RestoreGroupTestVariables": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "RestoreGroupFailedActionName", + "type": "string", + "value": "" + } + ] + } + }, + "Initialize_RestoreGroupFailedActionResponse": { + "runAfter": { + "Initialize_RestoreGroupFailedActionName": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "RestoreGroupFailedActionResponse", + "type": "Object", + "value": {} + } + ] + } + }, + "Initialize_RestoreGroupScopeExecutionStatus": { + "runAfter": { + "Initialize_RestoreGroupFailedActionResponse": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "RestoreGroupScopeExecutionStatus", + "type": "string", + "value": "InProgress" + } + ] + } + }, + "GroupTests_Scope": { + "actions": { + "Create_Group_Test": { + "actions": { + "Create_Group_Test_Actions": { + "actions": { + "Create_Group_Initial_Verify_Group_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['createGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Create_Group_Create_Group_Graph": { + "runAfter": { + "Create_Group_Initial_Verify_Group_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['createGroup']}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{triggerBody()?['initializationData']?['testGroupNames']?['createGroupNickname']}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Create_Group_Assign_Group_To_App": { + "runAfter": { + "Delay_After_Create_Group": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Create_Group_Create_Group_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_Group": { + "actions": { + "Create_Group_Query_Create_Group": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['createGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Create_Group_15s": { + "runAfter": { + "Create_Group_Query_Create_Group": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Create_Group_Assign_Group_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "DoUntil_Poll_Group_Provisioning_Logs": { + "actions": { + "Verify_Group_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_Group_Create_Group_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delay_Group_Provisioning_Logs_15s": { + "runAfter": { + "Verify_Group_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "DoUntil_Poll_Create_Group": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Delay_After_Create_Group": { + "runAfter": { + "Create_Group_Create_Group_Graph": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 1, + "unit": "Minute" + } + } + } + }, + "type": "Scope" + }, + "Capture_Failed_Group_Action_Details": { + "actions": { + "Get_Failed_Group_Actions": { + "type": "Compose", + "inputs": "@result('Create_Group_Test_Actions')" + }, + "Filter_Failed_Group_Actions": { + "runAfter": { + "Get_Failed_Group_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_Group_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_Failed_Group_Action_Name": { + "runAfter": { + "Filter_Failed_Group_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateGroupFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_Group_Actions')), 0), first(body('Filter_Failed_Group_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_Failed_Group_Action_Response": { + "runAfter": { + "Set_Failed_Group_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateGroupFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_Group_Actions')), 0), coalesce(first(body('Filter_Failed_Group_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_Create_Group_Test_Status_Failed": { + "runAfter": { + "Set_Failed_Group_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "CreateGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Create_Group_Test_Actions": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_Create_Group_Test_Status_Success": { + "runAfter": { + "Create_Group_Test_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", "inputs": { - "schema": { - "type": "object", - "properties": { - "initializationData": { - "type": "object", - "properties": { - "syncJobId": { - "type": "string" - }, - "testUserNames": { - "type": "object" - }, - "testGroupNames": { - "type": "object" - }, - "appRoles": { - "type": "object" - }, - "userAppRole": { - "type": "array" - }, - "schema": { - "type": "object" - } + "name": "CreateGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Create_Group_Test_Analyze_Provisioning_Results": { + "runAfter": { + "Create_Group_Test_Actions": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Failed_Group_Action_Details": [ + "Succeeded", + "Skipped" + ], + "Set_Create_Group_Test_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Verify_Group_Provisioning_Logs: ', coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Verify_Group_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('CreateGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('CreateGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0), false), 'success', 'Create_Group_Query_Create_Group: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('CreateGroupFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Create_Group_Query_Create_Group')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('CreateGroupFailedActionName')))" + } + }, + "Set_CreateGroup_Output_Variables": { + "actions": { + "Set_CreateGroupTestOutputs": { + "type": "SetVariable", + "inputs": { + "name": "CreateGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Create_Group_Test_Analyze_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Create_Group_Test_Analyze_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "Delete_Created_Group": { + "runAfter": { + "Set_CreateGroup_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Create_Group_Create_Group_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Create_Group_Test" + ] + } + ] + }, + "type": "If" + }, + "Update_Group_Test": { + "actions": { + "Update_Group_Test_Actions": { + "actions": { + "Update_Group_Verify_Creation_And_Provisioning": { + "actions": { + "Update_Group_Initial_Verify_Group_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_Group_Create_Group_Graph": { + "runAfter": { + "Update_Group_Initial_Verify_Group_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['updateGroup']}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{triggerBody()?['initializationData']?['testGroupNames']?['updateGroupNickname']}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_Group_Assign_To_App": { + "runAfter": { + "Delay_After_Update_Group_Create": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Update_Group_Create_Group_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_Group_DoUntil_Wait_For_Creation": { + "actions": { + "Update_Group_Check_Group_Create": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_Group_Delay_Creation_15s": { + "runAfter": { + "Update_Group_Check_Group_Create": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_Group_Assign_To_App": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Update_Group_DoUntil_Poll_Create_Provisioning": { + "actions": { + "Update_Group_Verify_Create_Provisioning": { + "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_Group_Create_Group_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_Group_Delay_Create_Provisioning_15s": { + "runAfter": { + "Update_Group_Verify_Create_Provisioning": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_Group_DoUntil_Wait_For_Creation": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Delay_After_Update_Group_Create": { + "runAfter": { + "Update_Group_Create_Group_Graph": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 1, + "unit": "Minute" + } + } + } + }, + "type": "Scope" + }, + "Capture_Update_Group_CreatePhase_Failed": { + "actions": { + "Get_Failed_CreatePhase_Group_Actions": { + "type": "Compose", + "inputs": "@result('Update_Group_Verify_Creation_And_Provisioning')" + }, + "Filter_Failed_CreatePhase_Group_Actions": { + "runAfter": { + "Get_Failed_CreatePhase_Group_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_CreatePhase_Group_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_UpdateGroup_CreatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_CreatePhase_Group_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_CreatePhase_Group_Actions')), 0), first(body('Filter_Failed_CreatePhase_Group_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_UpdateGroup_CreatePhase_Failed_Action_Response": { + "runAfter": { + "Set_UpdateGroup_CreatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_CreatePhase_Group_Actions')), 0), coalesce(first(body('Filter_Failed_CreatePhase_Group_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_UpdateGroup_CreatePhase_Status_Failed": { + "runAfter": { + "Set_UpdateGroup_CreatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Update_Group_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_UpdateGroup_CreatePhase_Status_Success": { + "runAfter": { + "Update_Group_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Update_Group_Analyze_Create_Provisioning": { + "runAfter": { + "Update_Group_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Update_Group_CreatePhase_Failed": [ + "Succeeded", + "Skipped" + ], + "Set_UpdateGroup_CreatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Group_Verify_Create_Provisioning: ', coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Group_Verify_Create_Provisioning: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0), false), 'success', 'Update_Group_Check_Group_Create: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Update_Group_Check_Group_Create')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('UpdateGroupFailedActionName')))" + } + }, + "Set_UpdateGroup_CreatePhase_Output_Variables": { + "actions": { + "Set_UpdateGroupTestOutputs_CreatePhase": { + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Update_Group_Analyze_Create_Provisioning')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Update_Group_Analyze_Create_Provisioning": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "Update_Group_Check_Create_Phase_Status": { + "actions": { + "Update_Group_Verify_Update_And_Provisioning": { + "actions": { + "Update_Group_Query_Group_By_Id": { + "type": "Http", + "inputs": { + "uri": "@concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_Group_Update_Group_Attributes": { + "runAfter": { + "Update_Group_Query_Group_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Update_Group_Create_Group_Graph')?['id']}", + "method": "PATCH", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{concat(triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '-Modified')}", + "description": "Updated Test Group Description" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Update_Group_DoUntil_Wait_For_Updates": { + "actions": { + "Update_Group_Query_Group_By_Id_After_Attributes_Update": { + "type": "Http", + "inputs": { + "uri": "@concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', concat(triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '-Modified'), '%22')", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Update_Group_Delay_Update_15s": { + "runAfter": { + "Update_Group_Query_Group_By_Id_After_Attributes_Update": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_Group_Update_Group_Attributes": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), contains(string(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')), '-Modified'))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Update_Group_DoUntil_Poll_Update_Provisioning": { + "actions": { + "Update_Group_Verify_Update_Provisioning": { + "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_Group_Create_Group_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_Group_Delay_Update_Provisioning_15s": { + "runAfter": { + "Update_Group_Verify_Update_Provisioning": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Update_Group_DoUntil_Wait_For_Updates": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "Capture_Update_Group_UpdatePhase_Failed": { + "actions": { + "Get_Failed_UpdatePhase_Group_Actions": { + "type": "Compose", + "inputs": "@result('Update_Group_Verify_Update_And_Provisioning')" + }, + "Filter_Failed_UpdatePhase_Group_Actions": { + "runAfter": { + "Get_Failed_UpdatePhase_Group_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_UpdatePhase_Group_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_UpdateGroup_UpdatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_UpdatePhase_Group_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_UpdatePhase_Group_Actions')), 0), first(body('Filter_Failed_UpdatePhase_Group_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_UpdateGroup_UpdatePhase_Failed_Action_Response": { + "runAfter": { + "Set_UpdateGroup_UpdatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_UpdatePhase_Group_Actions')), 0), coalesce(first(body('Filter_Failed_UpdatePhase_Group_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_UpdateGroup_UpdatePhase_Status_Failed": { + "runAfter": { + "Set_UpdateGroup_UpdatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Update_Group_Verify_Update_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_UpdateGroup_UpdatePhase_Status_Success": { + "runAfter": { + "Update_Group_Verify_Update_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Update_Group_Analyze_Update_Provisioning": { + "runAfter": { + "Update_Group_Verify_Update_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_Update_Group_UpdatePhase_Failed": [ + "Succeeded", + "Skipped" + ], + "Set_UpdateGroup_UpdatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Group_Verify_Update_Provisioning: ', coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Group_Verify_Update_Provisioning: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), 'success', 'Update_Group_Query_Group_By_Id_After_Attributes_Update: Group not found on SCIM server after update'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), null, 'SCIM verification failed: Group not found on SCIM server after update provisioning'), concat('Scope execution failed. Failed action: ', variables('UpdateGroupFailedActionName')))" + } + }, + "Set_UpdateGroup_UpdatePhase_Output_Variables": { + "actions": { + "Set_UpdateGroupTestOutputs_UpdatePhase": { + "type": "SetVariable", + "inputs": { + "name": "UpdateGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Update Phase] ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], '')), concat('FAILED - [Update Phase] Provisioning: ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Update_Group_Analyze_Update_Provisioning')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" } + } + } + } + }, + "runAfter": { + "Update_Group_Analyze_Update_Provisioning": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Set_UpdateGroup_CreatePhase_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('UpdateGroupTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Update_Group_Cleanup_Delete": { + "runAfter": { + "Update_Group_Check_Create_Phase_Status": [ + "Succeeded", + "Failed", + "TimedOut", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Update_Group_Create_Group_Graph')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + }, + "type": "Scope" + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Update_Group_Test" + ] + } + ] + }, + "type": "If" + }, + "Delete_Group_Test": { + "actions": { + "Delete_Group_Test_Actions": { + "actions": { + "Delete_Group_Verify_Creation_And_Provisioning": { + "actions": { + "Delete_Step1_Check_Group_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delete_Step2_Create_Group": { + "runAfter": { + "Delete_Step1_Check_Group_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup']}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{replace(triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '-', '')}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delete_Group_Assign_App_Role": { + "runAfter": { + "Delay_After_Delete_Group_Create": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", + "method": "POST", + "body": { + "principalId": "@{body('Delete_Step2_Create_Group')?['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/" + } + } + }, + "Delete_DoUntil_Wait_For_Group_Creation": { + "actions": { + "Delete_Step3_Fetch_Group_By_Filter": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delete_Group_Delay_Creation_15s": { + "runAfter": { + "Delete_Step3_Fetch_Group_By_Filter": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Delete_Group_Assign_App_Role": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Delete_Group_DoUntil_Poll_Create_Provisioning_Logs": { + "actions": { + "Delete_Group_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('Delete_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delete_Group_Delay_Create_Provisioning_Logs_15s": { + "runAfter": { + "Delete_Group_Verify_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Delete_DoUntil_Wait_For_Group_Creation": [ + "Succeeded" + ] + }, + "expression": "@greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Delay_After_Delete_Group_Create": { + "runAfter": { + "Delete_Step2_Create_Group": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 1, + "unit": "Minute" + } + } + } + }, + "type": "Scope" + }, + "Capture_DeleteGroup_CreatePhase_Failed_Actions": { + "actions": { + "Get_Failed_DeleteGroup_CreatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Delete_Group_Verify_Creation_And_Provisioning')" + }, + "Filter_Failed_DeleteGroup_CreatePhase_Actions": { + "runAfter": { + "Get_Failed_DeleteGroup_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_DeleteGroup_CreatePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_DeleteGroup_CreatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_DeleteGroup_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_DeleteGroup_CreatePhase_Actions')), 0), first(body('Filter_Failed_DeleteGroup_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_DeleteGroup_CreatePhase_Failed_Action_Response": { + "runAfter": { + "Set_DeleteGroup_CreatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_DeleteGroup_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_DeleteGroup_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_DeleteGroup_CreatePhase_Status_Failed": { + "runAfter": { + "Set_DeleteGroup_CreatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Delete_Group_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_DeleteGroup_CreatePhase_Status_Success": { + "runAfter": { + "Delete_Group_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Delete_Group_Analyze_Create_Provisioning_Results": { + "runAfter": { + "Delete_Group_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_DeleteGroup_CreatePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_DeleteGroup_CreatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Delete_Group_Verify_Create_Provisioning_Logs: ', coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Delete_Group_Verify_Create_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0), false), 'success', 'Delete_Step3_Fetch_Group_By_Filter: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DeleteGroupFailedActionName')))" + } + }, + "Set_DeleteGroup_CreatePhase_Output_Variables": { + "actions": { + "Set_DeleteGroupTestOutputs_CreatePhase": { + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Delete_Group_Analyze_Create_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "Delete_Group_Check_Create_Phase_Status": { + "actions": { + "Delete_Group_Verify_Delete_And_Provisioning": { + "actions": { + "Delete_Step4_Query_Group_By_Id": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delete_Step5_Delete_Group_By_Id": { + "runAfter": { + "Delete_Step4_Query_Group_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Delete_Step2_Create_Group')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delete_DoUntil_Wait_For_Group_Deletion": { + "actions": { + "Delete_Step6_Check_Group_Exists_Final": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delete_Group_Delay_Final_15s": { + "runAfter": { + "Delete_Step6_Check_Group_Exists_Final": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Delete_Step5_Delete_Group_By_Id": [ + "Succeeded" + ] + }, + "expression": "@equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Delete_Group_DoUntil_Poll_Delete_Provisioning_Logs": { + "actions": { + "Delete_Group_Verify_Delete_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('Delete_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Delete%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Delete_Group_Delay_Delete_Provisioning_Logs_15s": { + "runAfter": { + "Delete_Group_Verify_Delete_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Delete_DoUntil_Wait_For_Group_Deletion": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "expression": "@greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "Capture_DeleteGroup_DeletePhase_Failed_Actions": { + "actions": { + "Get_Failed_DeleteGroup_DeletePhase_Actions": { + "type": "Compose", + "inputs": "@result('Delete_Group_Verify_Delete_And_Provisioning')" + }, + "Filter_Failed_DeleteGroup_DeletePhase_Actions": { + "runAfter": { + "Get_Failed_DeleteGroup_DeletePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_DeleteGroup_DeletePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_DeleteGroup_DeletePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_DeleteGroup_DeletePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_DeleteGroup_DeletePhase_Actions')), 0), first(body('Filter_Failed_DeleteGroup_DeletePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_DeleteGroup_DeletePhase_Failed_Action_Response": { + "runAfter": { + "Set_DeleteGroup_DeletePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_DeleteGroup_DeletePhase_Actions')), 0), coalesce(first(body('Filter_Failed_DeleteGroup_DeletePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_DeleteGroup_DeletePhase_Status_Failed": { + "runAfter": { + "Set_DeleteGroup_DeletePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Delete_Group_Verify_Delete_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_DeleteGroup_DeletePhase_Status_Success": { + "runAfter": { + "Delete_Group_Verify_Delete_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Delete_Group_Analyze_Delete_Provisioning_Results": { + "runAfter": { + "Delete_Group_Verify_Delete_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_DeleteGroup_DeletePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_DeleteGroup_DeletePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'skipped'), 'Delete provisioning was skipped by the engine. Group deprovisioning cannot be skipped - the connector objectMapping flowTypes must include Delete', concat('Delete provisioning log status: ', coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'Delete_Group_Verify_Delete_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0), 'success', 'Delete_Step6_Check_Group_Exists_Final: Group still exists on SCIM server after deletion'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0), null, concat('SCIM verification failed: Group still exists on SCIM server after deletion. Resources count: ', string(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('DeleteGroupFailedActionName')))" + } + }, + "Set_DeleteGroup_Output_Variables": { + "actions": { + "Set_DeleteGroupTestOutputs_DeletePhase": { + "type": "SetVariable", + "inputs": { + "name": "DeleteGroupTestOutputs", + "value": { + "result": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Delete Phase] ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Delete Phase] Provisioning: ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Delete_Group_Analyze_Delete_Provisioning_Results": [ + "Succeeded" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Set_DeleteGroup_CreatePhase_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('DeleteGroupTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + } + }, + "type": "Scope" + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Delete_Group_Test" + ] + } + ] + }, + "type": "If" + }, + "Group_Update_Add_Member_Test": { + "actions": { + "Group_Update_Add_Member_Test_Actions": { + "actions": { + "Add_Member_Verify_Creation_And_Provisioning": { + "actions": { + "Add_DoUntil_Wait_For_Initial_Sync": { + "actions": { + "Add_Step4_Check_Group_And_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Add_Step4_Check_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['memberUser1'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Add_Delay_Initial_15s": { + "runAfter": { + "Add_Step4_Check_Group_And_User": [ + "Succeeded", + "Failed" + ], + "Add_Step4_Check_User": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Add_Assign_Group_To_App": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Add_Member_DoUntil_Poll_Group_Create_Provisioning_Logs": { + "actions": { + "Add_Member_Verify_Group_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('Add_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Add_Member_Delay_Group_Create_Provisioning_Logs_15s": { + "runAfter": { + "Add_Member_Verify_Group_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ], + "Add_Member_Verify_User_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + }, + "Add_Member_Verify_User_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('Add_Step3_Create_Member_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + }, + "runAfter": { + "Add_DoUntil_Wait_For_Initial_Sync": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Add_Assign_Group_To_App": { + "runAfter": { + "Delay_Before_Add_Group_AppRole": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Add_Step2_Create_Group')?['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/" + } + } + }, + "Add_Assign_User_To_App": { + "runAfter": { + "Add_Step3_Create_Member_User": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Add_Step3_Create_Member_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/" + } + } + }, + "Add_Step3_Create_Member_User": { + "runAfter": { + "Add_Step2_Create_Group": [ + "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']?['memberUser1']}", + "mailNickname": "@{replace(first(split(triggerBody()?['initializationData']?['testUserNames']?['memberUser1'],'@')),'.','')}", + "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser1']}", + "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']?['memberUser1Mail']}", + "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/" + } + } + }, + "Add_Step2_Create_Group": { + "runAfter": { + "Add_Step1_Check_Group_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['memberGroup']}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{replace(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-', '')}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Add_Step1_Check_Group_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Before_Add_Group_AppRole": { + "runAfter": { + "Add_Assign_User_To_App": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 1, + "unit": "Minute" + } + } + } + }, + "type": "Scope" + }, + "Add_Member_Analyze_Group_Create_Provisioning_Results": { + "runAfter": { + "Add_Member_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_AddMember_CreatePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_AddMember_CreatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningStatus": "@concat('Group: ', if(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'), ', User: ', if(greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'))", + "provisioningResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), concat('Group: ', if(equals(toLower(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_Group_Create_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), ', User: ', if(equals(toLower(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_User_Create_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(or(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), and(greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'))), json(concat('{\"group\":', string(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"user\":', string(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('AddMemberFailedActionResponse'))", + "overallResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), and(equals(toLower(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), and(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0), false)), 'success', concat('SCIM check: Group found=', string(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0)), ', User found=', string(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0)))), concat('Failed Action: ', variables('AddMemberFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0), false)), null, concat('SCIM verification failed: Group resources=', string(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]')))), ', User resources=', string(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('AddMemberFailedActionName')))" + } + }, + "Add_Member_Check_Create_Phase_Status": { + "actions": { + "Add_Member_Verify_Update_And_Provisioning": { + "actions": { + "Add_Step5_Add_User_To_Group": { + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Add_Step2_Create_Group')?['id']}/members/$ref", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Add_DoUntil_Wait_For_Member_Addition": { + "actions": { + "Add_Step6_Query_Group_With_Members": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Add_Delay_Member_15s": { + "runAfter": { + "Add_Step6_Query_Group_With_Members": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Add_Step5_Add_User_To_Group": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), and(greater(length(coalesce(first(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))?['members'], json('[]'))), 0), contains(string(coalesce(first(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))?['members'], json('[]'))), coalesce(first(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]')))?['id'], ''))))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Add_Step7_Query_Group_By_Id": { + "runAfter": { + "Add_DoUntil_Wait_For_Member_Addition": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Add_Member_DoUntil_Poll_Add_Member_Provisioning_Logs": { + "actions": { + "Add_Member_Verify_Add_Member_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('Add_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$orderby=activityDateTime%20desc&$top=1')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Add_Member_Delay_Add_Member_Provisioning_Logs_15s": { + "runAfter": { + "Add_Member_Verify_Add_Member_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Add_Step7_Query_Group_By_Id": [ + "Succeeded", + "Failed" + ] + }, + "expression": "@and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), and(empty(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue']), not(empty(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']))))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + } + }, + "type": "Scope" + }, + "Capture_AddMember_AddMemberPhase_Failed_Actions": { + "actions": { + "Get_Failed_AddMember_UpdatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Add_Member_Verify_Update_And_Provisioning')" + }, + "Filter_Failed_AddMember_UpdatePhase_Actions": { + "runAfter": { + "Get_Failed_AddMember_UpdatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_AddMember_UpdatePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_AddMember_UpdatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_AddMember_UpdatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_AddMember_UpdatePhase_Actions')), 0), first(body('Filter_Failed_AddMember_UpdatePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_AddMember_UpdatePhase_Failed_Action_Response": { + "runAfter": { + "Set_AddMember_UpdatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_AddMember_UpdatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_AddMember_UpdatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_AddMember_UpdatePhase_Status_Failed": { + "runAfter": { + "Set_AddMember_UpdatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Add_Member_Verify_Update_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_AddMember_UpdatePhase_Status_Success": { + "runAfter": { + "Add_Member_Verify_Update_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Add_Member_Analyze_Add_Member_Provisioning_Results": { + "runAfter": { + "Add_Member_Verify_Update_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_AddMember_AddMemberPhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_AddMember_UpdatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", + "provisioningResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_Add_Member_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Add_Member_Verify_Add_Member_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('AddMemberFailedActionResponse'))", + "overallResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), false), 'success', 'Add_Step6_Query_Group_With_Members: Member not found in group on SCIM server after add member'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Member not found in group after add member operation. Group resources=', string(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('AddMemberFailedActionName')))" + } + }, + "Set_AddMember_UpdatePhase_Output_Variables": { + "actions": { + "Set_AddMemberTestOutputs_UpdatePhase": { + "type": "SetVariable", + "inputs": { + "name": "AddMemberTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Add Member Phase] ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Add Member Phase] Provisioning: ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Add_Member_Analyze_Add_Member_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "Set_AddMember_CreatePhase_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('AddMemberTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Add_Cleanup_Delete_User": { + "runAfter": { + "Add_Member_Check_Create_Phase_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Add_Cleanup_Delete_Group": { + "runAfter": { + "Add_Cleanup_Delete_User": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Add_Step2_Create_Group')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Capture_AddMember_CreatePhase_Failed_Actions": { + "actions": { + "Get_Failed_AddMember_CreatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Add_Member_Verify_Creation_And_Provisioning')" + }, + "Filter_Failed_AddMember_CreatePhase_Actions": { + "runAfter": { + "Get_Failed_AddMember_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_AddMember_CreatePhase_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "Set_AddMember_CreatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_AddMember_CreatePhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberFailedActionName", + "value": "@{if(greater(length(body('Filter_Failed_AddMember_CreatePhase_Actions')), 0), first(body('Filter_Failed_AddMember_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "Set_AddMember_CreatePhase_Failed_Action_Response": { + "runAfter": { + "Set_AddMember_CreatePhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_AddMember_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_AddMember_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_AddMember_CreatePhase_Status_Failed": { + "runAfter": { + "Set_AddMember_CreatePhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Add_Member_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_AddMember_CreatePhase_Status_Success": { + "runAfter": { + "Add_Member_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "AddMemberScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Set_AddMember_CreatePhase_Output_Variables": { + "actions": { + "Set_AddMemberTestOutputs_CreatePhase": { + "type": "SetVariable", + "inputs": { + "name": "AddMemberTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningErrorDetails'], concat('Provisioning result: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], 'UNKNOWN'))))", + "scimCheck": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" + } + } + } + } + }, + "runAfter": { + "Add_Member_Analyze_Group_Create_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "type": "Scope" + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Group_Update_Add_Member_Test" + ] + } + ] + }, + "type": "If" + }, + "Group_Update_Remove_Member_Test": { + "actions": { + "Group_Update_Remove_Member_Test_Actions": { + "actions": { + "Remove_Member_Verify_Creation_And_Provisioning": { + "actions": { + "Remove_DoUntil_Wait_For_Initial_Member_Addition": { + "actions": { + "Remove_Step4a_Check_Group_With_Member": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Remove_Step4b_Check_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['memberUser2'], ''), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Remove_Delay_Initial_15s": { + "runAfter": { + "Remove_Step4a_Check_Group_With_Member": [ + "Succeeded", + "Failed" + ], + "Remove_Step4b_Check_User": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Remove_Step4_Add_User_To_Group": [ + "Succeeded" + ] + }, + "expression": "@and(and(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0)), and(not(empty(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))))), contains(string(body('Remove_Step4a_Check_Group_With_Member')), coalesce(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))?['id'], ''))))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Remove_Member_DoUntil_Poll_Group_Create_Provisioning_Logs": { + "actions": { + "Remove_Member_Verify_Group_Create_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('Remove_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$top=1&$orderby=activityDateTime%20desc')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Member_Verify_User_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('Remove_Step3_Create_Member_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Member_Delay_Group_Create_Provisioning_Logs_15s": { + "runAfter": { + "Remove_Member_Verify_Group_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ], + "Remove_Member_Verify_User_Create_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Remove_DoUntil_Wait_For_Initial_Member_Addition": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), and(not(empty(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'])), empty(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue'])))", + "limit": { + "count": 150, + "timeout": "PT15M" + }, + "type": "Until" + }, + "Remove_Step4_Add_User_To_Group": { + "runAfter": { + "Remove_Assign_Group_To_App": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}/members/$ref", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Assign_Group_To_App": { + "runAfter": { + "Delay_Before_Remove_Group_AppRole": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Remove_Step2_Create_Group')?['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/" + } + } + }, + "Remove_Assign_User_To_App": { + "runAfter": { + "Remove_Step3_Create_Member_User": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}/appRoleAssignments", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('Remove_Step3_Create_Member_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/" + } + } + }, + "Remove_Step3_Create_Member_User": { + "runAfter": { + "Remove_Step2_Create_Group": [ + "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']?['memberUser2']}", + "mailNickname": "@{replace(first(split(triggerBody()?['initializationData']?['testUserNames']?['memberUser2'],'@')),'.','')}", + "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser2']}", + "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']?['memberUser2Mail']}", + "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/" + } + } + }, + "Remove_Step2_Create_Group": { + "runAfter": { + "Remove_Step1_Check_Group_Exists": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove')}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{replace(concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], 'Remove'), '-', '')}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Step1_Check_Group_Exists": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Delay_Before_Remove_Group_AppRole": { + "runAfter": { + "Remove_Assign_User_To_App": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 1, + "unit": "Minute" + } + } + } + }, + "type": "Scope" + }, + "Remove_Member_Analyze_Group_Create_Provisioning_Results": { + "runAfter": { + "Remove_Member_Verify_Creation_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_RemoveMember_CreatePhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_RemoveMember_CreatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningStatus": "@concat('Group: ', if(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'), ', User: ', if(greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'))", + "provisioningResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), concat('Group: ', if(equals(toLower(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_Group_Create_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), ', User: ', if(equals(toLower(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_User_Create_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(or(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), and(greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'))), json(concat('{\"group\":', string(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"user\":', string(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('RemoveMemberFailedActionResponse'))", + "overallResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), and(equals(toLower(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), and(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0), false)), 'success', concat('SCIM check: Group found=', string(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0)), ', User found=', string(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0)))), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0), false)), null, concat('SCIM verification failed: Group resources=', string(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]')))), ', User resources=', string(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('RemoveMemberFailedActionName')))" + } + }, + "Remove_Member_Check_Create_Phase_Status": { + "actions": { + "Remove_Member_Verify_Update_And_Provisioning": { + "actions": { + "Remove_Step5_Remove_User_From_Group": { + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}/members/@{body('Remove_Step3_Create_Member_User')?['id']}/$ref", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_DoUntil_Wait_For_Member_Removal": { + "actions": { + "Remove_Step6_Query_Group_After_Remove": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Remove_Delay_Final_15s": { + "runAfter": { + "Remove_Step6_Query_Group_After_Remove": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Remove_Step5_Remove_User_From_Group": [ + "Succeeded" + ] + }, + "expression": "@and(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), and(not(empty(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))))), not(contains(string(body('Remove_Step6_Query_Group_After_Remove')), coalesce(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))?['id'], '')))))", + "limit": { + "count": 200, + "timeout": "PT20M" + }, + "type": "Until" + }, + "Remove_Step7_Query_Group_By_Id": { + "runAfter": { + "Remove_DoUntil_Wait_For_Member_Removal": [ + "Succeeded", + "TimedOut", + "Failed" + ] + }, + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove'), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Remove_Member_DoUntil_Poll_Remove_Member_Provisioning_Logs": { + "actions": { + "Remove_Member_Verify_Remove_Member_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('Remove_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$orderby=activityDateTime%20desc&$top=1')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Member_Delay_Remove_Member_Provisioning_Logs_15s": { + "runAfter": { + "Remove_Member_Verify_Remove_Member_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } + } + } + }, + "runAfter": { + "Remove_Step7_Query_Group_By_Id": [ + "Succeeded", + "Failed" + ] }, - "EnabledTests": { - "type": "string" + "expression": "@and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), and(not(empty(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue'])), empty(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'])))", + "limit": { + "count": 150, + "timeout": "PT15M" }, - "parentRunId": { - "type": "string" - } - } - } - } - } - }, - "actions": { - "Response_Immediate": { - "runAfter": { - "Initialize_All_Group_Test_Variables": [ - "Succeeded" - ] - }, - "type": "Response", - "kind": "Http", - "inputs": { - "statusCode": 202, - "body": { - "workflowRunId": "@workflow().run.name", - "workflowName": "@workflow().name", - "status": "Accepted", - "message": "GroupTests workflow started. Tests are running asynchronously." - } - } - }, - "Initialize_All_Group_Test_Variables": { - "runAfter": {}, - "type": "InitializeVariable", - "inputs": { - "variables": [ - { - "name": "CreateGroupFailedActionName", - "type": "string", - "value": "" + "type": "Until" + } }, - { - "name": "CreateGroupFailedActionResponse", - "type": "Object", - "value": {} - }, - { - "name": "CreateGroupScopeExecutionStatus", - "type": "string", - "value": "InProgress" - }, - { - "name": "CreateGroupTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "provisioningLogsResult": "", - "scimCheckResult": "", - "errorDetails": { - "provisioningLogs": "", - "scimCheck": "" - } + "type": "Scope" + }, + "Capture_RemoveMember_RemoveMemberPhase_Failed_Actions": { + "actions": { + "Get_Failed_RemoveMember_RemoveMemberPhase_Actions": { + "type": "Compose", + "inputs": "@result('Remove_Member_Verify_Update_And_Provisioning')" + }, + "Filter_Failed_RemoveMember_RemoveMemberPhase_Actions": { + "runAfter": { + "Get_Failed_RemoveMember_RemoveMemberPhase_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_RemoveMember_RemoveMemberPhase_Actions')", + "where": "@or(equals(item()?['status'], 'Failed'), equals(item()?['status'], 'TimedOut'))" } - }, - { - "name": "UpdateGroupFailedActionName", - "type": "string", - "value": "" - }, - { - "name": "UpdateGroupFailedActionResponse", - "type": "Object", - "value": {} - }, - { - "name": "UpdateGroupScopeExecutionStatus", - "type": "string", - "value": "InProgress" - }, - { - "name": "UpdateGroupTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "provisioningLogsResult": "", - "scimCheckResult": "", - "errorDetails": { - "provisioningLogs": "", - "scimCheck": "" - } + }, + "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_RemoveMember_RemoveMemberPhase_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberFailedActionName", + "value": "@{coalesce(first(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions'))?['name'], 'Unknown')}" + } + }, + "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Response": { + "runAfter": { + "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions')), 0), coalesce(first(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_RemoveMember_RemoveMemberPhase_Status_Failed": { + "runAfter": { + "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberScopeExecutionStatus", + "value": "Failed" } + } }, - { - "name": "DeleteGroupFailedActionName", - "type": "string", - "value": "" + "runAfter": { + "Remove_Member_Verify_Update_And_Provisioning": [ + "Failed" + ] }, - { - "name": "DeleteGroupFailedActionResponse", - "type": "Object", - "value": {} + "type": "Scope" + }, + "Set_RemoveMember_UpdatePhase_Status_Success": { + "runAfter": { + "Remove_Member_Verify_Update_And_Provisioning": [ + "Succeeded" + ] }, - { - "name": "DeleteGroupScopeExecutionStatus", - "type": "string", - "value": "InProgress" + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Remove_Member_Analyze_Remove_Member_Provisioning_Results": { + "runAfter": { + "Remove_Member_Verify_Update_And_Provisioning": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "Capture_RemoveMember_RemoveMemberPhase_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "Set_RemoveMember_UpdatePhase_Status_Success": [ + "Succeeded", + "Skipped" + ] }, - { - "name": "DeleteGroupTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "provisioningLogsResult": "", - "scimCheckResult": "", + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningStatus": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Failed'), 'FAILED', if(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND'))", + "provisioningResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_Remove_Member_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Remove_Member_Verify_Remove_Member_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RemoveMemberFailedActionResponse'))", + "overallResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", + "scimCheckResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), false), 'success', 'Remove_Step6_Query_Group_After_Remove: Group not found on SCIM server after member removal'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", + "scimCheckErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after member removal. Group resources=', string(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('RemoveMemberFailedActionName')))" + } + }, + "Set_RemoveMember_UpdatePhase_Output_Variables": { + "actions": { + "Set_RemoveMemberTestOutputs_UpdatePhase": { + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Remove Member Phase] ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Remove Member Phase] Provisioning: ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", "errorDetails": { - "provisioningLogs": "", - "scimCheck": "" + "provisioningLogs": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", + "scimCheck": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" } + } } + } }, - { - "name": "AddMemberFailedActionName", - "type": "string", - "value": "" - }, - { - "name": "AddMemberFailedActionResponse", - "type": "Object", - "value": {} - }, - { - "name": "AddMemberScopeExecutionStatus", - "type": "string", - "value": "InProgress" + "runAfter": { + "Remove_Member_Analyze_Remove_Member_Provisioning_Results": [ + "Succeeded", + "Failed" + ] }, + "type": "Scope" + } + }, + "runAfter": { + "Set_RemoveMemberCreatePhase_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ { - "name": "AddMemberTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "provisioningLogsResult": "", - "scimCheckResult": "", - "errorDetails": { - "provisioningLogs": "", - "scimCheck": "" - } - } + "equals": [ + "@toLower(variables('RemoveMemberTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "Remove_Cleanup_Delete_User": { + "runAfter": { + "Remove_Member_Check_Create_Phase_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Remove_Cleanup_Delete_Group": { + "runAfter": { + "Remove_Cleanup_Delete_User": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}", + "method": "DELETE", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "Capture_RemoveMember_CreatePhase_Failed_Actions": { + "actions": { + "Get_Failed_RemoveMember_CreatePhase_Actions": { + "type": "Compose", + "inputs": "@result('Remove_Member_Verify_Creation_And_Provisioning')" + }, + "Filter_Failed_RemoveMember_CreatePhase_Actions": { + "runAfter": { + "Get_Failed_RemoveMember_CreatePhase_Actions": [ + "Succeeded" + ] }, - { - "name": "RemoveMemberFailedActionName", - "type": "string", - "value": "" + "type": "Query", + "inputs": { + "from": "@outputs('Get_Failed_RemoveMember_CreatePhase_Actions')", + "where": "@or(equals(item()?['status'], 'Failed'), equals(item()?['status'], 'TimedOut'))" + } + }, + "Set_RemoveMember_CreatePhase_Failed_Action_Name": { + "runAfter": { + "Filter_Failed_RemoveMember_CreatePhase_Actions": [ + "Succeeded" + ] }, - { - "name": "RemoveMemberFailedActionResponse", - "type": "Object", - "value": {} + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberFailedActionName", + "value": "@{coalesce(first(body('Filter_Failed_RemoveMember_CreatePhase_Actions'))?['name'], 'Unknown')}" + } + }, + "Set_RemoveMember_CreatePhase_Failed_Action_Response": { + "runAfter": { + "Set_RemoveMember_CreatePhase_Failed_Action_Name": [ + "Succeeded" + ] }, - { - "name": "RemoveMemberScopeExecutionStatus", - "type": "string", - "value": "InProgress" + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberFailedActionResponse", + "value": "@if(greater(length(body('Filter_Failed_RemoveMember_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_RemoveMember_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "Set_RemoveMember_CreatePhase_Status_Failed": { + "runAfter": { + "Set_RemoveMember_CreatePhase_Failed_Action_Response": [ + "Succeeded" + ] }, - { - "name": "RemoveMemberTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "provisioningLogsResult": "", - "scimCheckResult": "", - "errorDetails": { - "provisioningLogs": "", - "scimCheck": "" - } + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "Remove_Member_Verify_Creation_And_Provisioning": [ + "Failed" + ] + }, + "type": "Scope" + }, + "Set_RemoveMember_CreatePhase_Status_Success": { + "runAfter": { + "Remove_Member_Verify_Creation_And_Provisioning": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberScopeExecutionStatus", + "value": "Succeeded" + } + }, + "Set_RemoveMemberCreatePhase_Output_Variables": { + "actions": { + "Set_RemoveMemberTestOutputs_CreatePhase": { + "type": "SetVariable", + "inputs": { + "name": "RemoveMemberTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningErrorDetails'], concat('Provisioning result: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], 'UNKNOWN'))))", + "scimCheck": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" } + } } - ] + } + }, + "runAfter": { + "Remove_Member_Analyze_Group_Create_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "type": "Scope" + } + }, + "else": { + "actions": {} + }, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Group_Update_Remove_Member_Test" + ] } + ] }, - "Initialize_PODGroupTestOutputs": { + "type": "If" + }, + "POD_Group_Test": { + "actions": { + "POD_Group_Test_Actions": { + "type": "Scope", + "actions": { + "POD_Create_Graph_Group": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://graph.microsoft.com/v1.0/groups", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{concat('PODTestGroup_', substring(guid(), 0, 8))}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{concat('podgrp', substring(guid(), 0, 8))}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_SCIM_Pre_Check_Group": { + "runAfter": { + "POD_Create_Graph_Group": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', body('POD_Create_Graph_Group')?['displayName'], '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_Group_Delay_After_Create": { + "runAfter": { + "POD_SCIM_Pre_Check_Group": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + }, + "POD_Group_Assign_AppRole": { + "runAfter": { + "POD_Group_Delay_After_Create": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', body('POD_Create_Graph_Group')?['id'], '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{body('POD_Create_Graph_Group')?['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_Group_Delay_After_AppRole": { + "runAfter": { + "POD_Group_Assign_AppRole": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 60, + "unit": "Second" + } + } + }, + "POD_Group_Get_Sync_Rules": { + "runAfter": { + "POD_Group_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_Group_Provision_On_Demand": { + "runAfter": { + "POD_Group_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_Group_Get_Sync_Rules')?['synchronizationRules'])?['id']}", + "subjects": [ + { + "objectId": "@{body('POD_Create_Graph_Group')?['id']}", + "objectTypeName": "Group" + } + ] + } + ] + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Group_Delay_After_Provision": { + "runAfter": { + "POD_Group_Provision_On_Demand": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 2, + "unit": "Minute" + } + } + }, + "POD_SCIM_Get_Group": { + "runAfter": { + "POD_Group_Delay_After_Provision": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', body('POD_Create_Graph_Group')?['displayName'], '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_Group_Until_Provisioning_Logs": { + "runAfter": { + "POD_SCIM_Get_Group": [ + "Succeeded", + "Failed" + ] + }, + "type": "Until", + "expression": "@greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "limit": { + "count": 30, + "timeout": "PT15M" + }, + "actions": { + "POD_Group_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_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Group_Delay_Log_Poll": { + "runAfter": { + "POD_Group_Verify_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + } + } + }, + "POD_Group_Analyze_Results": { "runAfter": { - "Initialize_All_Group_Test_Variables": ["Succeeded"] + "POD_Group_Test_Actions": [ + "Succeeded", + "Failed", + "TimedOut" + ] }, - "type": "InitializeVariable", + "type": "Compose", "inputs": { - "variables": [ - { - "name": "PODGroupTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "errorDetails": {} - } - } - ] + "testName": "POD_Group_Test", + "timestamp": "@utcNow()", + "graphGroupId": "@coalesce(body('POD_Create_Graph_Group')?['id'], '')", + "graphGroupName": "@coalesce(body('POD_Create_Graph_Group')?['displayName'], '')", + "appRoleAssigned": "@equals(outputs('POD_Group_Assign_AppRole')?['statusCode'], 201)", + "podStatusCode": "@coalesce(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 0)", + "podSucceeded": "@equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200)", + "scimGetStatusCode": "@coalesce(outputs('POD_SCIM_Get_Group')?['statusCode'], 0)", + "scimGroupFound": "@greater(length(coalesce(body('POD_SCIM_Get_Group')?['Resources'], json('[]'))), 0)", + "scimGroupId": "@coalesce(first(body('POD_SCIM_Get_Group')?['Resources'])?['id'], '')", + "provisioningLogFound": "@greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", + "provisioningLogStatus": "@coalesce(first(body('POD_Group_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'notFound')", + "overallResult": "@if(and(equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200), greater(length(coalesce(body('POD_SCIM_Get_Group')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED')" } - }, - "Initialize_RestoreGroupTestVariables": { + }, + "Set_PODGroupTestOutputs": { "runAfter": { - "Initialize_PODGroupTestOutputs": ["Succeeded"] + "POD_Group_Analyze_Results": [ + "Succeeded" + ] }, - "type": "InitializeVariable", + "type": "SetVariable", "inputs": { - "variables": [ - { - "name": "restoreEntraGroupId", - "type": "string", - "value": "" + "name": "PODGroupTestOutputs", + "value": { + "overallResult": "@coalesce(outputs('POD_Group_Analyze_Results')?['overallResult'], 'FAILED')", + "result": "@if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['appRoleAssigned'], false), false), 'FAILED - Action: POD_Group_Assign_AppRole - app role assignment failed', if(not(equals(coalesce(outputs('POD_Group_Analyze_Results')?['podStatusCode'], 0), 200)), concat('FAILED - Action: POD_Group_Provision_On_Demand - HTTP ', string(coalesce(outputs('POD_Group_Analyze_Results')?['podStatusCode'], 0))), if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['scimGroupFound'], false), false), 'FAILED - Action: POD_SCIM_Get_Group - group not found on SCIM endpoint', 'FAILED - Action: POD_Group_Verify_Provisioning_Logs - provisioning log not found'))))", + "summary": "@outputs('POD_Group_Analyze_Results')", + "errorDetails": { + "podResponse": "@if(not(equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200)), string(coalesce(body('POD_Group_Provision_On_Demand'), 'no response')), null)" + } + } + } + }, + "POD_Group_Cleanup_SCIM_Delete": { + "runAfter": { + "Set_PODGroupTestOutputs": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat(parameters('scimEndpoint'), '/Groups/', coalesce(first(body('POD_SCIM_Get_Group')?['Resources'])?['id'], 'not-found'))}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "POD_Group_Cleanup_Remove_AppRole": { + "runAfter": { + "POD_Group_Cleanup_SCIM_Delete": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', coalesce(body('POD_Create_Graph_Group')?['id'], 'not-found'), '/appRoleAssignments/', coalesce(body('POD_Group_Assign_AppRole')?['id'], 'not-found'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "POD_Group_Cleanup_Graph_Delete": { + "runAfter": { + "POD_Group_Cleanup_Remove_AppRole": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', coalesce(body('POD_Create_Graph_Group')?['id'], 'not-found'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + } + }, + "runAfter": {}, + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "POD_Group_Test" + ] + } + ] + }, + "type": "If" + }, + "Restore_Group_Test": { + "runAfter": {}, + "actions": { + "RestoreGroupTest_Actions": { + "type": "Scope", + "actions": { + "RestoreGroupTest_Phase1_Create_And_Assign": { + "type": "Scope", + "actions": { + "RestoreGroupTest_SCIM_Pre_Check": { + "type": "Http", + "inputs": { + "method": "GET", + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(coalesce(triggerBody()?['initializationData']?['testGroupNames']?['restoreGroup'], '')), '%22')}", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreGroupTest_Create_Entra_Group": { + "runAfter": { + "RestoreGroupTest_SCIM_Pre_Check": [ + "Succeeded", + "Failed" + ] }, - { - "name": "restoreGroupAppRoleAssignmentId", - "type": "string", - "value": "" + "type": "Http", + "inputs": { + "method": "POST", + "uri": "https://graph.microsoft.com/v1.0/groups", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "displayName": "@{coalesce(triggerBody()?['initializationData']?['testGroupNames']?['restoreGroup'], concat('RestoreGrp-', substring(guid(),0,8)))}", + "description": "@{parameters('defaultGroupProperties')['description']}", + "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", + "mailNickname": "@{concat('restoregrp', substring(guid(),0,8))}", + "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", + "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", + "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", + "visibility": "@{parameters('defaultGroupProperties')['visibility']}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_Set_EntraGroupId": { + "runAfter": { + "RestoreGroupTest_Create_Entra_Group": [ + "Succeeded" + ] }, - { - "name": "RestoreGroupTestOutputs", - "type": "Object", - "value": { - "overallResult": "SKIPPED", - "result": "SKIPPED", - "summary": null, - "detailedResults": [], - "errorDetails": { "summary": null } + "type": "SetVariable", + "inputs": { + "name": "restoreEntraGroupId", + "value": "@{body('RestoreGroupTest_Create_Entra_Group')?['id']}" + } + }, + "RestoreGroupTest_Delay_Before_AppRole": { + "runAfter": { + "RestoreGroupTest_Set_EntraGroupId": [ + "Succeeded" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + }, + "RestoreGroupTest_Assign_AppRole": { + "runAfter": { + "RestoreGroupTest_Delay_Before_AppRole": [ + "Succeeded" + ] + }, + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'), '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{variables('restoreEntraGroupId')}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_Set_AppRoleAssignmentId": { + "runAfter": { + "RestoreGroupTest_Assign_AppRole": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "restoreGroupAppRoleAssignmentId", + "value": "@{body('RestoreGroupTest_Assign_AppRole')?['id']}" + } + }, + "RestoreGroupTest_DoUntil_Verify_SCIM_Create": { + "runAfter": { + "RestoreGroupTest_Set_AppRoleAssignmentId": [ + "Succeeded" + ] + }, + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT50M" + }, + "actions": { + "RestoreGroupTest_SCIM_Check_Created": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreGroupTest_Delay_Create_15s": { + "runAfter": { + "RestoreGroupTest_SCIM_Check_Created": [ + "Succeeded", + "Failed" + ] + }, + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } } + } } - ] - } - }, - "GroupTests_Scope": { - "actions": { - "Create_Group_Test": { + }, + "RestoreGroupTest_DoUntil_Verify_Create_ProvisioningLog": { + "runAfter": { + "RestoreGroupTest_DoUntil_Verify_SCIM_Create": [ + "Succeeded", + "Failed" + ] + }, + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]'))), 0)", + "limit": { + "count": 20, + "timeout": "PT10M" + }, "actions": { - "Create_Group_Test_Actions": { - "actions": { - "Create_Group_Initial_Verify_Group_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['createGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Create_Group_Create_Group_Graph": { - "runAfter": { - "Create_Group_Initial_Verify_Group_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['createGroup']}", - "description": "@{parameters('defaultGroupProperties')['description']}", - "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", -"mailNickname": "@{triggerBody()?['initializationData']?['testGroupNames']?['createGroupNickname']}", - "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", - "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", - "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", - "visibility": "@{parameters('defaultGroupProperties')['visibility']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Create_Group_Assign_Group_To_App": { - "runAfter": { - "Delay_After_Create_Group": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Create_Group_Create_Group_Graph')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "DoUntil_Poll_Create_Group": { - "actions": { - "Create_Group_Query_Create_Group": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['createGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Create_Group_15s": { - "runAfter": { - "Create_Group_Query_Create_Group": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Create_Group_Assign_Group_To_App": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "DoUntil_Poll_Group_Provisioning_Logs": { - "actions": { - "Verify_Group_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_Group_Create_Group_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delay_Group_Provisioning_Logs_15s": { - "runAfter": { - "Verify_Group_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "DoUntil_Poll_Create_Group": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - }, - "Delay_After_Create_Group": { - "runAfter": { - "Create_Group_Create_Group_Graph": [ - "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 1, - "unit": "Minute" - } - } - } - }, - "type": "Scope" + "RestoreGroupTest_Get_Create_Log": { + "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', variables('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_Wait_Create_Log_30s": { + "runAfter": { + "RestoreGroupTest_Get_Create_Log": [ + "Succeeded", + "Failed" + ] }, - "Capture_Failed_Group_Action_Details": { - "actions": { - "Get_Failed_Group_Actions": { - "type": "Compose", - "inputs": "@result('Create_Group_Test_Actions')" - }, - "Filter_Failed_Group_Actions": { - "runAfter": { - "Get_Failed_Group_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_Group_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_Failed_Group_Action_Name": { - "runAfter": { - "Filter_Failed_Group_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateGroupFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_Group_Actions')), 0), first(body('Filter_Failed_Group_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_Failed_Group_Action_Response": { - "runAfter": { - "Set_Failed_Group_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateGroupFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_Group_Actions')), 0), coalesce(first(body('Filter_Failed_Group_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_Create_Group_Test_Status_Failed": { - "runAfter": { - "Set_Failed_Group_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "CreateGroupScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Create_Group_Test_Actions": [ - "Failed" - ] - }, - "type": "Scope" + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } + } + } + } + } + }, + "RestoreGroupTest_Capture_Phase1_Failed_Actions": { + "actions": { + "RestoreGroupTest_Get_Phase1_Failed_Actions": { + "type": "Compose", + "inputs": "@result('RestoreGroupTest_Phase1_Create_And_Assign')" + }, + "RestoreGroupTest_Filter_Phase1_Failed_Actions": { + "runAfter": { + "RestoreGroupTest_Get_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('RestoreGroupTest_Get_Phase1_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "RestoreGroupTest_Set_Phase1_Failed_Action_Name": { + "runAfter": { + "RestoreGroupTest_Filter_Phase1_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupFailedActionName", + "value": "@{if(greater(length(body('RestoreGroupTest_Filter_Phase1_Failed_Actions')), 0), first(body('RestoreGroupTest_Filter_Phase1_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "RestoreGroupTest_Set_Phase1_Failed_Action_Response": { + "runAfter": { + "RestoreGroupTest_Set_Phase1_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupFailedActionResponse", + "value": "@if(greater(length(body('RestoreGroupTest_Filter_Phase1_Failed_Actions')), 0), coalesce(first(body('RestoreGroupTest_Filter_Phase1_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "RestoreGroupTest_Set_Phase1_Status_Failed": { + "runAfter": { + "RestoreGroupTest_Set_Phase1_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupScopeExecutionStatus", + "value": "Failed" + } + } + }, + "runAfter": { + "RestoreGroupTest_Phase1_Create_And_Assign": [ + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreGroupTest_Set_Phase1_Status_Success": { + "runAfter": { + "RestoreGroupTest_Phase1_Create_And_Assign": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreGroupTest_Analyze_Phase1_Provisioning_Results": { + "runAfter": { + "RestoreGroupTest_Phase1_Create_And_Assign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreGroupTest_Capture_Phase1_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreGroupTest_Set_Phase1_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]'))), 0)", + "scimGroupCreated": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0)", + "provisioningResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]'))), 0), 'success', 'RestoreGroupTest_Get_Create_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), null, variables('RestoreGroupFailedActionResponse'))", + "overallResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "RestoreGroupTest_Set_Phase1_Output_Variables": { + "actions": { + "RestoreGroupTest_Set_RestoreGroupTestOutputs_Phase1": { + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['scimGroupCreated'], false), true)), 'success', if(startsWith(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['scimGroupCreated'], false), true), 'success', 'Group not found on SCIM server'))))", + "overallResult": "@outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['scimGroupCreated'], false), true), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningErrorDetails'], concat('Provisioning result: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['provisioningResult'], 'UNKNOWN'))))", + "scimCheck": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase1_Provisioning_Results')?['scimGroupCreated'], false), true), null, 'Group not found on SCIM server after provisioning')" + } + } + } + } + }, + "runAfter": { + "RestoreGroupTest_Analyze_Phase1_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreGroupTest_Check_Phase1_Status": { + "actions": { + "RestoreGroupTest_Phase2_Unassign": { + "runAfter": {}, + "type": "Scope", + "actions": { + "RestoreGroupTest_Unassign_AppRole": { + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/appRoleAssignedTo/', variables('restoreGroupAppRoleAssignmentId'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_DoUntil_Verify_SCIM_Gone": { + "runAfter": { + "RestoreGroupTest_Unassign_AppRole": [ + "Succeeded" + ] }, - "Set_Create_Group_Test_Status_Success": { - "runAfter": { - "Create_Group_Test_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", + "type": "Until", + "expression": "@equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT50M" + }, + "actions": { + "RestoreGroupTest_SCIM_Check_Gone": { + "type": "Http", "inputs": { - "name": "CreateGroupScopeExecutionStatus", - "value": "Succeeded" + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } } - }, - "Create_Group_Test_Analyze_Provisioning_Results": { + }, + "RestoreGroupTest_Delay_Gone_15s": { "runAfter": { - "Create_Group_Test_Actions": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Failed_Group_Action_Details": [ - "Succeeded", - "Skipped" - ], - "Set_Create_Group_Test_Status_Success": [ - "Succeeded", - "Skipped" - ] + "RestoreGroupTest_SCIM_Check_Gone": [ + "Succeeded", + "Failed" + ] }, - "type": "Compose", + "type": "Wait", "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Verify_Group_Provisioning_Logs: ', coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Verify_Group_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('CreateGroupFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('CreateGroupFailedActionResponse'))", - "overallResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Verify_Group_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Verify_Group_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0), false), 'success', 'Create_Group_Query_Create_Group: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('CreateGroupFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('CreateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Create_Group_Query_Create_Group')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Create_Group_Query_Create_Group')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('CreateGroupFailedActionName')))" + "interval": { + "count": 15, + "unit": "Second" + } } + } + } + }, + "RestoreGroupTest_DoUntil_Verify_Delete_ProvisioningLog": { + "runAfter": { + "RestoreGroupTest_DoUntil_Verify_SCIM_Gone": [ + "Succeeded", + "Failed", + "TimedOut" + ] }, - "Set_CreateGroup_Output_Variables": { - "actions": { - "Set_CreateGroupTestOutputs": { - "type": "SetVariable", - "inputs": { - "name": "CreateGroupTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Create_Group_Test_Analyze_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Create_Group_Test_Analyze_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Create_Group_Test_Analyze_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]'))), 0)", + "limit": { + "count": 150, + "timeout": "PT15M" }, - "Delete_Created_Group": { - "runAfter": { - "Set_CreateGroup_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, + "actions": { + "RestoreGroupTest_Get_Delete_Log": { "type": "Http", "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Create_Group_Create_Group_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + "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('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Delete%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } } - } - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] + }, + "RestoreGroupTest_Wait_Delete_Log_15s": { + "runAfter": { + "RestoreGroupTest_Get_Delete_Log": [ + "Succeeded", + "Failed" + ] }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "GroupTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Create_Group_Test" - ] - } - ] + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } } - ] - }, - "type": "If" - }, - "Update_Group_Test": { + } + } + } + } + }, + "RestoreGroupTest_Capture_Phase2_Failed_Actions": { "actions": { - "Update_Group_Test_Actions": { - "actions": { - "Update_Group_Verify_Creation_And_Provisioning": { - "actions": { - "Update_Group_Initial_Verify_Group_Exists": { - "type": "Http", - "inputs": { -"uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_Group_Create_Group_Graph": { - "runAfter": { - "Update_Group_Initial_Verify_Group_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { -"displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['updateGroup']}", - "description": "@{parameters('defaultGroupProperties')['description']}", - "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", -"mailNickname": "@{triggerBody()?['initializationData']?['testGroupNames']?['updateGroupNickname']}", - "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", - "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", - "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", - "visibility": "@{parameters('defaultGroupProperties')['visibility']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Group_Assign_To_App": { - "runAfter": { - "Delay_After_Update_Group_Create": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Update_Group_Create_Group_Graph')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Group_DoUntil_Wait_For_Creation": { - "actions": { - "Update_Group_Check_Group_Create": { - "type": "Http", - "inputs": { -"uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_Group_Delay_Creation_15s": { - "runAfter": { - "Update_Group_Check_Group_Create": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_Group_Assign_To_App": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Update_Group_DoUntil_Poll_Create_Provisioning": { - "actions": { - "Update_Group_Verify_Create_Provisioning": { - "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_Group_Create_Group_Graph')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Group_Delay_Create_Provisioning_15s": { - "runAfter": { - "Update_Group_Verify_Create_Provisioning": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_Group_DoUntil_Wait_For_Creation": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - }, - "Delay_After_Update_Group_Create": { - "runAfter": { - "Update_Group_Create_Group_Graph": [ - "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 1, - "unit": "Minute" - } - } - } - }, - "type": "Scope" - }, - "Capture_Update_Group_CreatePhase_Failed": { - "actions": { - "Get_Failed_CreatePhase_Group_Actions": { - "type": "Compose", - "inputs": "@result('Update_Group_Verify_Creation_And_Provisioning')" - }, - "Filter_Failed_CreatePhase_Group_Actions": { - "runAfter": { - "Get_Failed_CreatePhase_Group_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_CreatePhase_Group_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_UpdateGroup_CreatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_CreatePhase_Group_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_CreatePhase_Group_Actions')), 0), first(body('Filter_Failed_CreatePhase_Group_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_UpdateGroup_CreatePhase_Failed_Action_Response": { - "runAfter": { - "Set_UpdateGroup_CreatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_CreatePhase_Group_Actions')), 0), coalesce(first(body('Filter_Failed_CreatePhase_Group_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_UpdateGroup_CreatePhase_Status_Failed": { - "runAfter": { - "Set_UpdateGroup_CreatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Update_Group_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_UpdateGroup_CreatePhase_Status_Success": { - "runAfter": { - "Update_Group_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Update_Group_Analyze_Create_Provisioning": { - "runAfter": { - "Update_Group_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Update_Group_CreatePhase_Failed": [ - "Succeeded", - "Skipped" - ], - "Set_UpdateGroup_CreatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Group_Verify_Create_Provisioning: ', coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Group_Verify_Create_Provisioning: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateGroupFailedActionResponse'))", - "overallResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Group_Verify_Create_Provisioning')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Group_Verify_Create_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0), false), 'success', 'Update_Group_Check_Group_Create: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Update_Group_Check_Group_Create')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Update_Group_Check_Group_Create')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('UpdateGroupFailedActionName')))" - } - }, - "Set_UpdateGroup_CreatePhase_Output_Variables": { - "actions": { - "Set_UpdateGroupTestOutputs_CreatePhase": { - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Update_Group_Analyze_Create_Provisioning')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Create_Provisioning')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Update_Group_Analyze_Create_Provisioning": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "Update_Group_Check_Create_Phase_Status": { - "actions": { - "Update_Group_Verify_Update_And_Provisioning": { - "actions": { - "Update_Group_Query_Group_By_Id": { - "type": "Http", - "inputs": { -"uri": "@concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '%22')", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_Group_Update_Group_Attributes": { - "runAfter": { - "Update_Group_Query_Group_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Update_Group_Create_Group_Graph')?['id']}", - "method": "PATCH", - "headers": { - "Content-Type": "application/json" - }, - "body": { -"displayName": "@{concat(triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '-Modified')}", - "description": "Updated Test Group Description" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Update_Group_DoUntil_Wait_For_Updates": { - "actions": { - "Update_Group_Query_Group_By_Id_After_Attributes_Update": { - "type": "Http", - "inputs": { -"uri": "@concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', concat(triggerBody()?['initializationData']?['testGroupNames']?['updateGroup'], '-Modified'), '%22')", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Update_Group_Delay_Update_15s": { - "runAfter": { - "Update_Group_Query_Group_By_Id_After_Attributes_Update": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_Group_Update_Group_Attributes": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), contains(string(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')), '-Modified'))", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Update_Group_DoUntil_Poll_Update_Provisioning": { - "actions": { - "Update_Group_Verify_Update_Provisioning": { - "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_Group_Create_Group_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_Group_Delay_Update_Provisioning_15s": { - "runAfter": { - "Update_Group_Verify_Update_Provisioning": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Update_Group_DoUntil_Wait_For_Updates": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_Update_Group_UpdatePhase_Failed": { - "actions": { - "Get_Failed_UpdatePhase_Group_Actions": { - "type": "Compose", - "inputs": "@result('Update_Group_Verify_Update_And_Provisioning')" - }, - "Filter_Failed_UpdatePhase_Group_Actions": { - "runAfter": { - "Get_Failed_UpdatePhase_Group_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_UpdatePhase_Group_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_UpdateGroup_UpdatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_UpdatePhase_Group_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_UpdatePhase_Group_Actions')), 0), first(body('Filter_Failed_UpdatePhase_Group_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_UpdateGroup_UpdatePhase_Failed_Action_Response": { - "runAfter": { - "Set_UpdateGroup_UpdatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_UpdatePhase_Group_Actions')), 0), coalesce(first(body('Filter_Failed_UpdatePhase_Group_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_UpdateGroup_UpdatePhase_Status_Failed": { - "runAfter": { - "Set_UpdateGroup_UpdatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Update_Group_Verify_Update_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_UpdateGroup_UpdatePhase_Status_Success": { - "runAfter": { - "Update_Group_Verify_Update_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Update_Group_Analyze_Update_Provisioning": { - "runAfter": { - "Update_Group_Verify_Update_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_Update_Group_UpdatePhase_Failed": [ - "Succeeded", - "Skipped" - ], - "Set_UpdateGroup_UpdatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Update_Group_Verify_Update_Provisioning: ', coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Update_Group_Verify_Update_Provisioning: NO_LOGS_FOUND'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), equals(toLower(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('UpdateGroupFailedActionResponse'))", - "overallResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Update_Group_Verify_Update_Provisioning')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Update_Group_Verify_Update_Provisioning')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), 'success', 'Update_Group_Query_Group_By_Id_After_Attributes_Update: Group not found on SCIM server after update'), concat('Failed Action: ', variables('UpdateGroupFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('UpdateGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Update_Group_Query_Group_By_Id_After_Attributes_Update')?['Resources'], json('[]'))), 0), null, 'SCIM verification failed: Group not found on SCIM server after update provisioning'), concat('Scope execution failed. Failed action: ', variables('UpdateGroupFailedActionName')))" - } - }, - "Set_UpdateGroup_UpdatePhase_Output_Variables": { - "actions": { - "Set_UpdateGroupTestOutputs_UpdatePhase": { - "type": "SetVariable", - "inputs": { - "name": "UpdateGroupTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Update Phase] ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], '')), concat('FAILED - [Update Phase] Provisioning: ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Update_Group_Analyze_Update_Provisioning')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Update_Group_Analyze_Update_Provisioning')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Update_Group_Analyze_Update_Provisioning": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Set_UpdateGroup_CreatePhase_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('UpdateGroupTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Update_Group_Cleanup_Delete": { - "runAfter": { - "Update_Group_Check_Create_Phase_Status": [ - "Succeeded", - "Failed", - "TimedOut", - "Skipped" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Update_Group_Create_Group_Graph')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - }, - "type": "Scope" + "RestoreGroupTest_Get_Phase2_Failed_Actions": { + "type": "Compose", + "inputs": "@result('RestoreGroupTest_Phase2_Unassign')" + }, + "RestoreGroupTest_Filter_Phase2_Failed_Actions": { + "runAfter": { + "RestoreGroupTest_Get_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@outputs('RestoreGroupTest_Get_Phase2_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" + } + }, + "RestoreGroupTest_Set_Phase2_Failed_Action_Name": { + "runAfter": { + "RestoreGroupTest_Filter_Phase2_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupFailedActionName", + "value": "@{if(greater(length(body('RestoreGroupTest_Filter_Phase2_Failed_Actions')), 0), first(body('RestoreGroupTest_Filter_Phase2_Failed_Actions'))?['name'], 'Unknown_Action')}" + } + }, + "RestoreGroupTest_Set_Phase2_Failed_Action_Response": { + "runAfter": { + "RestoreGroupTest_Set_Phase2_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupFailedActionResponse", + "value": "@if(greater(length(body('RestoreGroupTest_Filter_Phase2_Failed_Actions')), 0), coalesce(first(body('RestoreGroupTest_Filter_Phase2_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" + } + }, + "RestoreGroupTest_Set_Phase2_Status_Failed": { + "runAfter": { + "RestoreGroupTest_Set_Phase2_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupScopeExecutionStatus", + "value": "Failed" } + } }, - "else": { - "actions": {} + "runAfter": { + "RestoreGroupTest_Phase2_Unassign": [ + "Failed" + ] }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] - }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "GroupTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Update_Group_Test" - ] - } - ] - } - ] + "type": "Scope" + }, + "RestoreGroupTest_Set_Phase2_Status_Success": { + "runAfter": { + "RestoreGroupTest_Phase2_Unassign": [ + "Succeeded" + ] }, - "type": "If" - }, - "Delete_Group_Test": { + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreGroupTest_Analyze_Phase2_Provisioning_Results": { + "runAfter": { + "RestoreGroupTest_Phase2_Unassign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreGroupTest_Capture_Phase2_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreGroupTest_Set_Phase2_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]'))), 0)", + "scimGroupGone": "@equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)", + "provisioningResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]'))), 0), 'success', 'RestoreGroupTest_Get_Delete_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), null, variables('RestoreGroupFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0), 'success', 'RestoreGroupTest: Group still present on SCIM server after unassign'), concat('Failed Action: ', variables('RestoreGroupFailedActionName')))", + "overallResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]'))), 0), equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "RestoreGroupTest_Set_Phase2_Output_Variables": { "actions": { - "Delete_Group_Test_Actions": { - "actions": { - "Delete_Group_Verify_Creation_And_Provisioning": { - "actions": { - "Delete_Step1_Check_Group_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delete_Step2_Create_Group": { - "runAfter": { - "Delete_Step1_Check_Group_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup']}", - "description": "@{parameters('defaultGroupProperties')['description']}", - "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", - "mailNickname": "@{replace(triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '-', '')}", - "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", - "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", - "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", - "visibility": "@{parameters('defaultGroupProperties')['visibility']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delete_Group_Assign_App_Role": { - "runAfter": { - "Delay_After_Delete_Group_Create": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", - "method": "POST", - "body": { - "principalId": "@{body('Delete_Step2_Create_Group')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delete_DoUntil_Wait_For_Group_Creation": { - "actions": { - "Delete_Step3_Fetch_Group_By_Filter": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delete_Group_Delay_Creation_15s": { - "runAfter": { - "Delete_Step3_Fetch_Group_By_Filter": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Delete_Group_Assign_App_Role": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Delete_Group_DoUntil_Poll_Create_Provisioning_Logs": { - "actions": { - "Delete_Group_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('Delete_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delete_Group_Delay_Create_Provisioning_Logs_15s": { - "runAfter": { - "Delete_Group_Verify_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Delete_DoUntil_Wait_For_Group_Creation": [ - "Succeeded" - ] - }, - "expression": "@greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - }, - "Delay_After_Delete_Group_Create": { - "runAfter": { - "Delete_Step2_Create_Group": [ - "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 1, - "unit": "Minute" - } - } - } - }, - "type": "Scope" - }, - "Capture_DeleteGroup_CreatePhase_Failed_Actions": { - "actions": { - "Get_Failed_DeleteGroup_CreatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Delete_Group_Verify_Creation_And_Provisioning')" - }, - "Filter_Failed_DeleteGroup_CreatePhase_Actions": { - "runAfter": { - "Get_Failed_DeleteGroup_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_DeleteGroup_CreatePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_DeleteGroup_CreatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_DeleteGroup_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_DeleteGroup_CreatePhase_Actions')), 0), first(body('Filter_Failed_DeleteGroup_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_DeleteGroup_CreatePhase_Failed_Action_Response": { - "runAfter": { - "Set_DeleteGroup_CreatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_DeleteGroup_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_DeleteGroup_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_DeleteGroup_CreatePhase_Status_Failed": { - "runAfter": { - "Set_DeleteGroup_CreatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Delete_Group_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_DeleteGroup_CreatePhase_Status_Success": { - "runAfter": { - "Delete_Group_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Delete_Group_Analyze_Create_Provisioning_Results": { - "runAfter": { - "Delete_Group_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_DeleteGroup_CreatePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_DeleteGroup_CreatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Delete_Group_Verify_Create_Provisioning_Logs: ', coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Delete_Group_Verify_Create_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteGroupFailedActionResponse'))", - "overallResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Delete_Group_Verify_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0), false), 'success', 'Delete_Step3_Fetch_Group_By_Filter: Group not found on SCIM server after provisioning'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after provisioning. SCIM query returned ', string(length(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['Resources'], json('[]')))), ' resources (expected >= 1). totalResults: ', string(coalesce(body('Delete_Step3_Fetch_Group_By_Filter')?['totalResults'], 'N/A')))), concat('Scope execution failed. Failed action: ', variables('DeleteGroupFailedActionName')))" - } - }, - "Set_DeleteGroup_CreatePhase_Output_Variables": { - "actions": { - "Set_DeleteGroupTestOutputs_CreatePhase": { - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Delete_Group_Analyze_Create_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - }, - "Delete_Group_Check_Create_Phase_Status": { - "actions": { - "Delete_Group_Verify_Delete_And_Provisioning": { - "actions": { - "Delete_Step4_Query_Group_By_Id": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delete_Step5_Delete_Group_By_Id": { - "runAfter": { - "Delete_Step4_Query_Group_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Delete_Step2_Create_Group')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delete_DoUntil_Wait_For_Group_Deletion": { - "actions": { - "Delete_Step6_Check_Group_Exists_Final": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['deleteGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delete_Group_Delay_Final_15s": { - "runAfter": { - "Delete_Step6_Check_Group_Exists_Final": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Delete_Step5_Delete_Group_By_Id": [ - "Succeeded" - ] - }, - "expression": "@equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0)", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Delete_Group_DoUntil_Poll_Delete_Provisioning_Logs": { - "actions": { - "Delete_Group_Verify_Delete_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('Delete_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Delete%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Delete_Group_Delay_Delete_Provisioning_Logs_15s": { - "runAfter": { - "Delete_Group_Verify_Delete_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Delete_DoUntil_Wait_For_Group_Deletion": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "expression": "@greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_DeleteGroup_DeletePhase_Failed_Actions": { - "actions": { - "Get_Failed_DeleteGroup_DeletePhase_Actions": { - "type": "Compose", - "inputs": "@result('Delete_Group_Verify_Delete_And_Provisioning')" - }, - "Filter_Failed_DeleteGroup_DeletePhase_Actions": { - "runAfter": { - "Get_Failed_DeleteGroup_DeletePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_DeleteGroup_DeletePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_DeleteGroup_DeletePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_DeleteGroup_DeletePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_DeleteGroup_DeletePhase_Actions')), 0), first(body('Filter_Failed_DeleteGroup_DeletePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_DeleteGroup_DeletePhase_Failed_Action_Response": { - "runAfter": { - "Set_DeleteGroup_DeletePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_DeleteGroup_DeletePhase_Actions')), 0), coalesce(first(body('Filter_Failed_DeleteGroup_DeletePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_DeleteGroup_DeletePhase_Status_Failed": { - "runAfter": { - "Set_DeleteGroup_DeletePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Delete_Group_Verify_Delete_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_DeleteGroup_DeletePhase_Status_Success": { - "runAfter": { - "Delete_Group_Verify_Delete_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Delete_Group_Analyze_Delete_Provisioning_Results": { - "runAfter": { - "Delete_Group_Verify_Delete_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_DeleteGroup_DeletePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_DeleteGroup_DeletePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', if(equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'skipped'), 'Delete provisioning was skipped by the engine. Group deprovisioning cannot be skipped - the connector objectMapping flowTypes must include Delete', concat('Delete provisioning log status: ', coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'Delete_Group_Verify_Delete_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('DeleteGroupFailedActionResponse'))", - "overallResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Delete_Group_Verify_Delete_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0), 'success', 'Delete_Step6_Check_Group_Exists_Final: Group still exists on SCIM server after deletion'), concat('Failed Action: ', variables('DeleteGroupFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('DeleteGroupScopeExecutionStatus'), 'Succeeded'), if(equals(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]'))), 0), null, concat('SCIM verification failed: Group still exists on SCIM server after deletion. Resources count: ', string(length(coalesce(body('Delete_Step6_Check_Group_Exists_Final')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('DeleteGroupFailedActionName')))" - } - }, - "Set_DeleteGroup_Output_Variables": { - "actions": { - "Set_DeleteGroupTestOutputs_DeletePhase": { - "type": "SetVariable", - "inputs": { - "name": "DeleteGroupTestOutputs", - "value": { - "result": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Delete Phase] ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Delete Phase] Provisioning: ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Delete_Group_Analyze_Delete_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Delete_Group_Analyze_Delete_Provisioning_Results": [ - "Succeeded" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Set_DeleteGroup_CreatePhase_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('DeleteGroupTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - } - }, - "type": "Scope" + "RestoreGroupTest_Set_RestoreGroupTestOutputs_Phase2": { + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Unassign Phase] ', coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Unassign Phase] Provisioning: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed'))", + "scimCheck": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase2_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, 'Group still present on SCIM server after unassign')" + } + } } + } }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] - }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "GroupTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Delete_Group_Test" - ] - } - ] - } - ] + "runAfter": { + "RestoreGroupTest_Analyze_Phase2_Provisioning_Results": [ + "Succeeded", + "Failed" + ] }, - "type": "If" - }, - "Group_Update_Add_Member_Test": { + "type": "Scope" + }, + "RestoreGroupTest_Check_Phase2_Status": { "actions": { - "Group_Update_Add_Member_Test_Actions": { - "actions": { - "Add_Member_Verify_Creation_And_Provisioning": { - "actions": { - "Add_DoUntil_Wait_For_Initial_Sync": { - "actions": { - "Add_Step4_Check_Group_And_User": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Add_Step4_Check_User": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['memberUser1'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Add_Delay_Initial_15s": { - "runAfter": { - "Add_Step4_Check_Group_And_User": [ - "Succeeded", - "Failed" - ], - "Add_Step4_Check_User": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Add_Assign_Group_To_App": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0))", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Add_Member_DoUntil_Poll_Group_Create_Provisioning_Logs": { - "actions": { - "Add_Member_Verify_Group_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('Add_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Member_Delay_Group_Create_Provisioning_Logs_15s": { - "runAfter": { - "Add_Member_Verify_Group_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ], - "Add_Member_Verify_User_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - }, - "Add_Member_Verify_User_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('Add_Step3_Create_Member_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - }, - "runAfter": { - "Add_DoUntil_Wait_For_Initial_Sync": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0))", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - }, - "Add_Assign_Group_To_App": { - "runAfter": { - "Delay_Before_Add_Group_AppRole": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Add_Step2_Create_Group')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Assign_User_To_App": { - "runAfter": { - "Add_Step3_Create_Member_User": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Add_Step3_Create_Member_User')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Step3_Create_Member_User": { - "runAfter": { - "Add_Step2_Create_Group": [ - "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']?['memberUser1']}", - "mailNickname": "@{replace(first(split(triggerBody()?['initializationData']?['testUserNames']?['memberUser1'],'@')),'.','')}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser1']}", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser1Mail']}", - "givenName": "MemberUser1", - "surname": "Test", - "passwordProfile": { - "forceChangePasswordNextSignIn": true, - "password": "@{concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8))}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Step2_Create_Group": { - "runAfter": { - "Add_Step1_Check_Group_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "displayName": "@{triggerBody()?['initializationData']?['testGroupNames']?['memberGroup']}", - "description": "@{parameters('defaultGroupProperties')['description']}", - "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", - "mailNickname": "@{replace(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-', '')}", - "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", - "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", - "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", - "visibility": "@{parameters('defaultGroupProperties')['visibility']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Step1_Check_Group_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Before_Add_Group_AppRole": { - "runAfter": { - "Add_Assign_User_To_App": [ - "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 1, - "unit": "Minute" - } - } - } - }, - "type": "Scope" - }, - "Add_Member_Analyze_Group_Create_Provisioning_Results": { - "runAfter": { - "Add_Member_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_AddMember_CreatePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_AddMember_CreatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningStatus": "@concat('Group: ', if(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'), ', User: ', if(greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'))", - "provisioningResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), concat('Group: ', if(equals(toLower(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_Group_Create_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), ', User: ', if(equals(toLower(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_User_Create_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(or(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), and(greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'))), json(concat('{\"group\":', string(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"user\":', string(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('AddMemberFailedActionResponse'))", - "overallResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), and(equals(toLower(coalesce(first(body('Add_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Add_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), and(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0), false)), 'success', concat('SCIM check: Group found=', string(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0)), ', User found=', string(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0)))), concat('Failed Action: ', variables('AddMemberFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]'))), 0), false)), null, concat('SCIM verification failed: Group resources=', string(length(coalesce(body('Add_Step4_Check_Group_And_User')?['Resources'], json('[]')))), ', User resources=', string(length(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('AddMemberFailedActionName')))" - } - }, - "Add_Member_Check_Create_Phase_Status": { - "actions": { - "Add_Member_Verify_Update_And_Provisioning": { - "actions": { - "Add_Step5_Add_User_To_Group": { - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Add_Step2_Create_Group')?['id']}/members/$ref", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_DoUntil_Wait_For_Member_Addition": { - "actions": { - "Add_Step6_Query_Group_With_Members": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Add_Delay_Member_15s": { - "runAfter": { - "Add_Step6_Query_Group_With_Members": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Add_Step5_Add_User_To_Group": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), and(greater(length(coalesce(first(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))?['members'], json('[]'))), 0), contains(string(coalesce(first(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))?['members'], json('[]'))), coalesce(first(coalesce(body('Add_Step4_Check_User')?['Resources'], json('[]')))?['id'], ''))))", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Add_Step7_Query_Group_By_Id": { - "runAfter": { - "Add_DoUntil_Wait_For_Member_Addition": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Add_Member_DoUntil_Poll_Add_Member_Provisioning_Logs": { - "actions": { - "Add_Member_Verify_Add_Member_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('Add_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$orderby=activityDateTime%20desc&$top=1')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Member_Delay_Add_Member_Provisioning_Logs_15s": { - "runAfter": { - "Add_Member_Verify_Add_Member_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Add_Step7_Query_Group_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "expression": "@and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), and(empty(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue']), not(empty(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue']))))", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_AddMember_AddMemberPhase_Failed_Actions": { - "actions": { - "Get_Failed_AddMember_UpdatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Add_Member_Verify_Update_And_Provisioning')" - }, - "Filter_Failed_AddMember_UpdatePhase_Actions": { - "runAfter": { - "Get_Failed_AddMember_UpdatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_AddMember_UpdatePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_AddMember_UpdatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_AddMember_UpdatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_AddMember_UpdatePhase_Actions')), 0), first(body('Filter_Failed_AddMember_UpdatePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_AddMember_UpdatePhase_Failed_Action_Response": { - "runAfter": { - "Set_AddMember_UpdatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_AddMember_UpdatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_AddMember_UpdatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_AddMember_UpdatePhase_Status_Failed": { - "runAfter": { - "Set_AddMember_UpdatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Add_Member_Verify_Update_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_AddMember_UpdatePhase_Status_Success": { - "runAfter": { - "Add_Member_Verify_Update_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Add_Member_Analyze_Add_Member_Provisioning_Results": { - "runAfter": { - "Add_Member_Verify_Update_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_AddMember_AddMemberPhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_AddMember_UpdatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND')", - "provisioningResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Add_Member_Verify_Add_Member_Provisioning_Logs: ', coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Add_Member_Verify_Add_Member_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('AddMemberFailedActionResponse'))", - "overallResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Add_Member_Verify_Add_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), false), 'success', 'Add_Step6_Query_Group_With_Members: Member not found in group on SCIM server after add member'), concat('Failed Action: ', variables('AddMemberFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('AddMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Member not found in group after add member operation. Group resources=', string(length(coalesce(body('Add_Step6_Query_Group_With_Members')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('AddMemberFailedActionName')))" - } - }, - "Set_AddMember_UpdatePhase_Output_Variables": { - "actions": { - "Set_AddMemberTestOutputs_UpdatePhase": { - "type": "SetVariable", - "inputs": { - "name": "AddMemberTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Add Member Phase] ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Add Member Phase] Provisioning: ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Add_Member_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Add_Member_Analyze_Add_Member_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Set_AddMember_CreatePhase_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('AddMemberTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Add_Cleanup_Delete_User": { - "runAfter": { - "Add_Member_Check_Create_Phase_Status": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Add_Step3_Create_Member_User')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Add_Cleanup_Delete_Group": { - "runAfter": { - "Add_Cleanup_Delete_User": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Add_Step2_Create_Group')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Capture_AddMember_CreatePhase_Failed_Actions": { - "actions": { - "Get_Failed_AddMember_CreatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Add_Member_Verify_Creation_And_Provisioning')" - }, - "Filter_Failed_AddMember_CreatePhase_Actions": { - "runAfter": { - "Get_Failed_AddMember_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_AddMember_CreatePhase_Actions')", - "where": "@equals(item()?['status'], 'Failed')" - } - }, - "Set_AddMember_CreatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_AddMember_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberFailedActionName", - "value": "@{if(greater(length(body('Filter_Failed_AddMember_CreatePhase_Actions')), 0), first(body('Filter_Failed_AddMember_CreatePhase_Actions'))?['name'], 'Unknown_Action')}" - } - }, - "Set_AddMember_CreatePhase_Failed_Action_Response": { - "runAfter": { - "Set_AddMember_CreatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_AddMember_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_AddMember_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_AddMember_CreatePhase_Status_Failed": { - "runAfter": { - "Set_AddMember_CreatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Add_Member_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_AddMember_CreatePhase_Status_Success": { - "runAfter": { - "Add_Member_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "AddMemberScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Set_AddMember_CreatePhase_Output_Variables": { - "actions": { - "Set_AddMemberTestOutputs_CreatePhase": { - "type": "SetVariable", - "inputs": { - "name": "AddMemberTestOutputs", - "value": { - "result": "@if(and(not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'UNKNOWN')), equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), if(and(not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'MISSING')), not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed'))), 'success', if(startsWith(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], '')))), if(startsWith(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'UNKNOWN')), and(not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'MISSING')), not(contains(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed')))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Add_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Add_Member_Analyze_Group_Create_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } + "RestoreGroupTest_Phase3_Reassign": { + "runAfter": {}, + "type": "Scope", + "actions": { + "RestoreGroupTest_Reassign_AppRole": { + "type": "Http", + "inputs": { + "method": "POST", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'), '/appRoleAssignments')}", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "principalId": "@{variables('restoreEntraGroupId')}", + "resourceId": "@{parameters('servicePrincipalId')}", + "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_DoUntil_Verify_SCIM_Restored": { + "runAfter": { + "RestoreGroupTest_Reassign_AppRole": [ + "Succeeded" + ] }, - "type": "Scope" - } - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)", + "limit": { + "count": 200, + "timeout": "PT50M" }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "GroupTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Group_Update_Add_Member_Test" - ] - } - ] - } - ] - }, - "type": "If" - }, - "Group_Update_Remove_Member_Test": { - "actions": { - "Group_Update_Remove_Member_Test_Actions": { "actions": { - "Remove_Member_Verify_Creation_And_Provisioning": { - "actions": { - "Remove_DoUntil_Wait_For_Initial_Member_Addition": { - "actions": { - "Remove_Step4a_Check_Group_With_Member": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Remove_Step4b_Check_User": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?filter=userName%20eq%20%22', coalesce(triggerBody()?['initializationData']?['testUserNames']?['memberUser2'], ''), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Remove_Delay_Initial_15s": { - "runAfter": { - "Remove_Step4a_Check_Group_With_Member": [ - "Succeeded", - "Failed" - ], - "Remove_Step4b_Check_User": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Remove_Step4_Add_User_To_Group": [ - "Succeeded" - ] - }, - "expression": "@and(and(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0)), and(not(empty(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))))), contains(string(body('Remove_Step4a_Check_Group_With_Member')), coalesce(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))?['id'], ''))))", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Remove_Member_DoUntil_Poll_Group_Create_Provisioning_Logs": { - "actions": { - "Remove_Member_Verify_Group_Create_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('Remove_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$top=1&$orderby=activityDateTime%20desc')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Member_Verify_User_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('Remove_Step3_Create_Member_User')?['id'], '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Member_Delay_Group_Create_Provisioning_Logs_15s": { - "runAfter": { - "Remove_Member_Verify_Group_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ], - "Remove_Member_Verify_User_Create_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Remove_DoUntil_Wait_For_Initial_Member_Addition": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), and(not(empty(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'])), empty(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue'])))", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - }, - "Remove_Step4_Add_User_To_Group": { - "runAfter": { - "Remove_Assign_Group_To_App": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}/members/$ref", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "@@odata.id": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Assign_Group_To_App": { - "runAfter": { - "Delay_Before_Remove_Group_AppRole": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/servicePrincipals/@{parameters('servicePrincipalId')}/appRoleAssignedTo", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Remove_Step2_Create_Group')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Assign_User_To_App": { - "runAfter": { - "Remove_Step3_Create_Member_User": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}/appRoleAssignments", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "principalId": "@{body('Remove_Step3_Create_Member_User')?['id']}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Step3_Create_Member_User": { - "runAfter": { - "Remove_Step2_Create_Group": [ - "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']?['memberUser2']}", - "mailNickname": "@{replace(first(split(triggerBody()?['initializationData']?['testUserNames']?['memberUser2'],'@')),'.','')}", - "userPrincipalName": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser2']}", - "mail": "@{triggerBody()?['initializationData']?['testUserNames']?['memberUser2Mail']}", - "givenName": "MemberUser2", - "surname": "Test", - "passwordProfile": { - "forceChangePasswordNextSignIn": true, - "password": "@{concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8))}" - } - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Step2_Create_Group": { - "runAfter": { - "Remove_Step1_Check_Group_Exists": [ - "Succeeded" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "displayName": "@{concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove')}", - "description": "@{parameters('defaultGroupProperties')['description']}", - "mailEnabled": "@parameters('defaultGroupProperties')['mailEnabled']", - "mailNickname": "@{replace(concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], 'Remove'), '-', '')}", - "securityEnabled": "@parameters('defaultGroupProperties')['securityEnabled']", - "groupTypes": "@parameters('defaultGroupProperties')['groupTypes']", - "isAssignableToRole": "@parameters('defaultGroupProperties')['isAssignableToRole']", - "visibility": "@{parameters('defaultGroupProperties')['visibility']}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Step1_Check_Group_Exists": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Delay_Before_Remove_Group_AppRole": { - "runAfter": { - "Remove_Assign_User_To_App": [ - "Succeeded" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 1, - "unit": "Minute" - } - } - } - }, - "type": "Scope" - }, - "Remove_Member_Analyze_Group_Create_Provisioning_Results": { - "runAfter": { - "Remove_Member_Verify_Creation_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_RemoveMember_CreatePhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_RemoveMember_CreatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningStatus": "@concat('Group: ', if(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'), ', User: ', if(greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS'))", - "provisioningResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), concat('Group: ', if(equals(toLower(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_Group_Create_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), ', User: ', if(equals(toLower(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_User_Create_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN')))), 'NO_LOGS_FOUND'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(or(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), and(greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure'))), json(concat('{\"group\":', string(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), ',\"user\":', string(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null)), '}')), null), variables('RemoveMemberFailedActionResponse'))", - "overallResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(and(greater(length(coalesce(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'], json('[]'))), 0), greater(length(coalesce(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'], json('[]'))), 0)), and(equals(toLower(coalesce(first(body('Remove_Member_Verify_Group_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), equals(toLower(coalesce(first(body('Remove_Member_Verify_User_Create_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'))), and(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0), false)), 'success', concat('SCIM check: Group found=', string(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0)), ', User found=', string(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0)))), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(coalesce(greater(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]'))), 0), false), coalesce(greater(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))), 0), false)), null, concat('SCIM verification failed: Group resources=', string(length(coalesce(body('Remove_Step4a_Check_Group_With_Member')?['Resources'], json('[]')))), ', User resources=', string(length(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('RemoveMemberFailedActionName')))" - } - }, - "Remove_Member_Check_Create_Phase_Status": { - "actions": { - "Remove_Member_Verify_Update_And_Provisioning": { - "actions": { - "Remove_Step5_Remove_User_From_Group": { - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}/members/@{body('Remove_Step3_Create_Member_User')?['id']}/$ref", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_DoUntil_Wait_For_Member_Removal": { - "actions": { - "Remove_Step6_Query_Group_After_Remove": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Remove_Delay_Final_15s": { - "runAfter": { - "Remove_Step6_Query_Group_After_Remove": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Remove_Step5_Remove_User_From_Group": [ - "Succeeded" - ] - }, - "expression": "@and(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), and(not(empty(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]'))))), not(contains(string(body('Remove_Step6_Query_Group_After_Remove')), coalesce(first(coalesce(body('Remove_Step4b_Check_User')?['Resources'], json('[]')))?['id'], '')))))", - "limit": { - "count": 200, - "timeout": "PT60M" - }, - "type": "Until" - }, - "Remove_Step7_Query_Group_By_Id": { - "runAfter": { - "Remove_DoUntil_Wait_For_Member_Removal": [ - "Succeeded", - "TimedOut", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', concat(triggerBody()?['initializationData']?['testGroupNames']?['memberGroup'], '-Remove'), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "Remove_Member_DoUntil_Poll_Remove_Member_Provisioning_Logs": { - "actions": { - "Remove_Member_Verify_Remove_Member_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('Remove_Step2_Create_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27Update%27%20and%20modifiedProperties/any(p:%20p/displayName%20eq%20%27members%27)&$orderby=activityDateTime%20desc&$top=1')}", - "method": "GET", - "headers": { - "Accept": "application/json" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Member_Delay_Remove_Member_Provisioning_Logs_15s": { - "runAfter": { - "Remove_Member_Verify_Remove_Member_Provisioning_Logs": [ - "Succeeded", - "Failed" - ] - }, - "type": "Wait", - "inputs": { - "interval": { - "count": 15, - "unit": "Second" - } - } - } - }, - "runAfter": { - "Remove_Step7_Query_Group_By_Id": [ - "Succeeded", - "Failed" - ] - }, - "expression": "@and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), and(not(empty(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['oldValue'])), empty(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['modifiedProperties'][0]?['newValue'])))", - "limit": { - "count": 150, - "timeout": "PT40M" - }, - "type": "Until" - } - }, - "type": "Scope" - }, - "Capture_RemoveMember_RemoveMemberPhase_Failed_Actions": { - "actions": { - "Get_Failed_RemoveMember_RemoveMemberPhase_Actions": { - "type": "Compose", - "inputs": "@result('Remove_Member_Verify_Update_And_Provisioning')" - }, - "Filter_Failed_RemoveMember_RemoveMemberPhase_Actions": { - "runAfter": { - "Get_Failed_RemoveMember_RemoveMemberPhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_RemoveMember_RemoveMemberPhase_Actions')", - "where": "@or(equals(item()?['status'], 'Failed'), equals(item()?['status'], 'TimedOut'))" - } - }, - "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_RemoveMember_RemoveMemberPhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberFailedActionName", - "value": "@{coalesce(first(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions'))?['name'], 'Unknown')}" - } - }, - "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Response": { - "runAfter": { - "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions')), 0), coalesce(first(body('Filter_Failed_RemoveMember_RemoveMemberPhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_RemoveMember_RemoveMemberPhase_Status_Failed": { - "runAfter": { - "Set_RemoveMember_RemoveMemberPhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Remove_Member_Verify_Update_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_RemoveMember_UpdatePhase_Status_Success": { - "runAfter": { - "Remove_Member_Verify_Update_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberScopeExecutionStatus", - "value": "Succeeded" - } - }, - "Remove_Member_Analyze_Remove_Member_Provisioning_Results": { - "runAfter": { - "Remove_Member_Verify_Update_And_Provisioning": [ - "Succeeded", - "Failed", - "TimedOut" - ], - "Capture_RemoveMember_RemoveMemberPhase_Failed_Actions": [ - "Succeeded", - "Skipped" - ], - "Set_RemoveMember_UpdatePhase_Status_Success": [ - "Succeeded", - "Skipped" - ] - }, - "type": "Compose", - "inputs": { - "provisioningLogsCount": "@length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]')))", - "hasProvisioningLogs": "@greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningStatus": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Failed'), 'FAILED', if(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'), 'NO_LOGS_FOUND'))", - "provisioningResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), if(equals(toLower(coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success'), 'success', concat('Remove_Member_Verify_Remove_Member_Provisioning_Logs: ', coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'UNKNOWN'))), 'Remove_Member_Verify_Remove_Member_Provisioning_Logs: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", - "provisioningErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status']), 'failure')), first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['errorInformation'], null), variables('RemoveMemberFailedActionResponse'))", - "overallResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(and(and(greater(length(coalesce(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'], json('[]'))), 0), equals(toLower(coalesce(first(body('Remove_Member_Verify_Remove_Member_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], '')), 'success')), greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')", - "scimCheckResult": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), false), 'success', 'Remove_Step6_Query_Group_After_Remove: Group not found on SCIM server after member removal'), concat('Failed Action: ', variables('RemoveMemberFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RemoveMemberScopeExecutionStatus'), 'Succeeded'), if(coalesce(greater(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]'))), 0), false), null, concat('SCIM verification failed: Group not found on SCIM server after member removal. Group resources=', string(length(coalesce(body('Remove_Step6_Query_Group_After_Remove')?['Resources'], json('[]')))))), concat('Scope execution failed. Failed action: ', variables('RemoveMemberFailedActionName')))" - } - }, - "Set_RemoveMember_UpdatePhase_Output_Variables": { - "actions": { - "Set_RemoveMemberTestOutputs_UpdatePhase": { - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberTestOutputs", - "value": { - "result": "@if(and(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success', if(startsWith(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Remove Member Phase] ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Remove Member Phase] Provisioning: ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(contains(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), not(contains(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'failure'))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Remove_Member_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Remove_Member_Analyze_Remove_Member_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" - } - }, - "runAfter": { - "Set_RemoveMemberCreatePhase_Output_Variables": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@toLower(variables('RemoveMemberTestOutputs')['overallResult'])", - "passed" - ] - } - ] - }, - "type": "If" - }, - "Remove_Cleanup_Delete_User": { - "runAfter": { - "Remove_Member_Check_Create_Phase_Status": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/users/@{body('Remove_Step3_Create_Member_User')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Remove_Cleanup_Delete_Group": { - "runAfter": { - "Remove_Cleanup_Delete_User": [ - "Succeeded", - "Failed", - "Skipped" - ] - }, - "type": "Http", - "inputs": { - "uri": "https://graph.microsoft.com/v1.0/groups/@{body('Remove_Step2_Create_Group')?['id']}", - "method": "DELETE", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "Capture_RemoveMember_CreatePhase_Failed_Actions": { - "actions": { - "Get_Failed_RemoveMember_CreatePhase_Actions": { - "type": "Compose", - "inputs": "@result('Remove_Member_Verify_Creation_And_Provisioning')" - }, - "Filter_Failed_RemoveMember_CreatePhase_Actions": { - "runAfter": { - "Get_Failed_RemoveMember_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "Query", - "inputs": { - "from": "@outputs('Get_Failed_RemoveMember_CreatePhase_Actions')", - "where": "@or(equals(item()?['status'], 'Failed'), equals(item()?['status'], 'TimedOut'))" - } - }, - "Set_RemoveMember_CreatePhase_Failed_Action_Name": { - "runAfter": { - "Filter_Failed_RemoveMember_CreatePhase_Actions": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberFailedActionName", - "value": "@{coalesce(first(body('Filter_Failed_RemoveMember_CreatePhase_Actions'))?['name'], 'Unknown')}" - } - }, - "Set_RemoveMember_CreatePhase_Failed_Action_Response": { - "runAfter": { - "Set_RemoveMember_CreatePhase_Failed_Action_Name": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberFailedActionResponse", - "value": "@if(greater(length(body('Filter_Failed_RemoveMember_CreatePhase_Actions')), 0), coalesce(first(body('Filter_Failed_RemoveMember_CreatePhase_Actions'))?['outputs'], json('{}')), json('{}'))" - } - }, - "Set_RemoveMember_CreatePhase_Status_Failed": { - "runAfter": { - "Set_RemoveMember_CreatePhase_Failed_Action_Response": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberScopeExecutionStatus", - "value": "Failed" - } - } - }, - "runAfter": { - "Remove_Member_Verify_Creation_And_Provisioning": [ - "Failed" - ] - }, - "type": "Scope" - }, - "Set_RemoveMember_CreatePhase_Status_Success": { - "runAfter": { - "Remove_Member_Verify_Creation_And_Provisioning": [ - "Succeeded" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberScopeExecutionStatus", - "value": "Succeeded" - } + "RestoreGroupTest_SCIM_Check_Restored": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "RestoreGroupTest_Delay_Restored_15s": { + "runAfter": { + "RestoreGroupTest_SCIM_Check_Restored": [ + "Succeeded", + "Failed" + ] }, - "Set_RemoveMemberCreatePhase_Output_Variables": { - "actions": { - "Set_RemoveMemberTestOutputs_CreatePhase": { - "type": "SetVariable", - "inputs": { - "name": "RemoveMemberTestOutputs", - "value": { - "result": "@if(and(not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'UNKNOWN')), equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success')), if(and(not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'MISSING')), not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed'))), 'success', if(startsWith(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], '')))), if(startsWith(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Create Phase] ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Create Phase] Provisioning: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''))))", - "overallResult": "@outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['overallResult']", - "provisioningLogsResult": "@if(and(not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'UNKNOWN')), and(not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'MISSING')), not(contains(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'Failed')))), 'success', 'failure')", - "scimCheckResult": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", - "errorDetails": { - "provisioningLogs": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed - unable to retrieve provisioning error details'))", - "scimCheck": "@if(equals(coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, coalesce(outputs('Remove_Member_Analyze_Group_Create_Provisioning_Results')?['scimCheckErrorDetails'], 'Analyze step failed - unable to retrieve SCIM error details'))" - } - } - } - } - }, - "runAfter": { - "Remove_Member_Analyze_Group_Create_Provisioning_Results": [ - "Succeeded", - "Failed" - ] - }, - "type": "Scope" + "type": "Wait", + "inputs": { + "interval": { + "count": 15, + "unit": "Second" + } } + } + } + }, + "RestoreGroupTest_DoUntil_Verify_Restore_ProvisioningLog": { + "runAfter": { + "RestoreGroupTest_DoUntil_Verify_SCIM_Restored": [ + "Succeeded", + "Failed", + "TimedOut" + ] }, - "type": "Scope" - } - }, - "else": { - "actions": {} - }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] + "type": "Until", + "expression": "@greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0)", + "limit": { + "count": 20, + "timeout": "PT10M" }, - { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "GroupTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Group_Update_Remove_Member_Test" - ] - } - ] - } - ] - }, - "type": "If" - }, - "POD_Group_Test": { - "actions": { - "POD_Group_Test_Actions": { - "type": "Scope", "actions": { - "POD_Create_Graph_Group": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "https://graph.microsoft.com/v1.0/groups", - "headers": { "Content-Type": "application/json" }, - "body": { - "displayName": "@{concat('PODTestGroup_', substring(guid(), 0, 8))}", - "mailNickname": "@{concat('podgrp', substring(guid(), 0, 8))}", - "mailEnabled": false, - "securityEnabled": true, - "groupTypes": [] - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Group_Delay_After_Create": { - "runAfter": { "POD_Create_Graph_Group": ["Succeeded"] }, - "type": "Wait", - "inputs": { "interval": { "count": 30, "unit": "Second" } } - }, - "POD_Group_Assign_AppRole": { - "runAfter": { "POD_Group_Delay_After_Create": ["Succeeded"] }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', body('POD_Create_Graph_Group')?['id'], '/appRoleAssignments')}", - "headers": { "Content-Type": "application/json" }, - "body": { - "principalId": "@{body('POD_Create_Graph_Group')?['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_Group_Delay_After_AppRole": { - "runAfter": { "POD_Group_Assign_AppRole": ["Succeeded"] }, - "type": "Wait", - "inputs": { "interval": { "count": 60, "unit": "Second" } } - }, - "POD_Group_Get_Sync_Rules": { - "runAfter": { "POD_Group_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_Group_Provision_On_Demand": { - "runAfter": { "POD_Group_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_Group_Get_Sync_Rules')?['synchronizationRules'])?['id']}", - "subjects": [ - { - "objectId": "@{body('POD_Create_Graph_Group')?['id']}", - "objectTypeName": "Group" - } - ] - } - ] - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Group_Delay_After_Provision": { - "runAfter": { "POD_Group_Provision_On_Demand": ["Succeeded", "Failed"] }, - "type": "Wait", - "inputs": { "interval": { "count": 2, "unit": "Minute" } } - }, - "POD_SCIM_Get_Group": { - "runAfter": { "POD_Group_Delay_After_Provision": ["Succeeded"] }, - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', body('POD_Create_Graph_Group')?['displayName'], '%22')}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } + "RestoreGroupTest_Get_Restore_Log": { + "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', variables('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Create%27')}", + "method": "GET", + "headers": { + "Accept": "application/json" + }, + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } + } + }, + "RestoreGroupTest_Wait_Restore_Log_30s": { + "runAfter": { + "RestoreGroupTest_Get_Restore_Log": [ + "Succeeded", + "Failed" + ] }, - "POD_Group_Until_Provisioning_Logs": { - "runAfter": { "POD_SCIM_Get_Group": ["Succeeded", "Failed"] }, - "type": "Until", - "expression": "@greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "limit": { "count": 30, "timeout": "PT15M" }, - "actions": { - "POD_Group_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_Group')?['id'], '%27%20and%20provisioningAction%20eq%20%27create%27')}", - "headers": { "Accept": "application/json" }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "POD_Group_Delay_Log_Poll": { - "runAfter": { "POD_Group_Verify_Provisioning_Logs": ["Succeeded", "Failed"] }, - "type": "Wait", - "inputs": { "interval": { "count": 30, "unit": "Second" } } - } - } + "type": "Wait", + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } } + } } - }, - "POD_Group_Analyze_Results": { - "runAfter": { "POD_Group_Test_Actions": ["Succeeded", "Failed", "TimedOut"] }, + } + } + }, + "RestoreGroupTest_Capture_Phase3_Failed_Actions": { + "actions": { + "RestoreGroupTest_Get_Phase3_Failed_Actions": { "type": "Compose", + "inputs": "@result('RestoreGroupTest_Phase3_Reassign')" + }, + "RestoreGroupTest_Filter_Phase3_Failed_Actions": { + "runAfter": { + "RestoreGroupTest_Get_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, + "type": "Query", "inputs": { - "testName": "POD_Group_Test", - "timestamp": "@utcNow()", - "graphGroupId": "@coalesce(body('POD_Create_Graph_Group')?['id'], '')", - "graphGroupName": "@coalesce(body('POD_Create_Graph_Group')?['displayName'], '')", - "appRoleAssigned": "@equals(outputs('POD_Group_Assign_AppRole')?['statusCode'], 201)", - "podStatusCode": "@coalesce(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 0)", - "podSucceeded": "@equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200)", - "scimGetStatusCode": "@coalesce(outputs('POD_SCIM_Get_Group')?['statusCode'], 0)", - "scimGroupFound": "@greater(length(coalesce(body('POD_SCIM_Get_Group')?['Resources'], json('[]'))), 0)", - "scimGroupId": "@coalesce(first(body('POD_SCIM_Get_Group')?['Resources'])?['id'], '')", - "provisioningLogFound": "@greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", - "provisioningLogStatus": "@coalesce(first(body('POD_Group_Verify_Provisioning_Logs')?['value'])?['provisioningStatusInfo']?['status'], 'notFound')", - "overallResult": "@if(and(equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200), greater(length(coalesce(body('POD_SCIM_Get_Group')?['Resources'], json('[]'))), 0), greater(length(coalesce(body('POD_Group_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED')" + "from": "@outputs('RestoreGroupTest_Get_Phase3_Failed_Actions')", + "where": "@equals(item()?['status'], 'Failed')" } - }, - "Set_PODGroupTestOutputs": { - "runAfter": { "POD_Group_Analyze_Results": ["Succeeded"] }, + }, + "RestoreGroupTest_Set_Phase3_Failed_Action_Name": { + "runAfter": { + "RestoreGroupTest_Filter_Phase3_Failed_Actions": [ + "Succeeded" + ] + }, "type": "SetVariable", "inputs": { - "name": "PODGroupTestOutputs", - "value": { - "overallResult": "@coalesce(outputs('POD_Group_Analyze_Results')?['overallResult'], 'FAILED')", - "result": "@if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['appRoleAssigned'], false), false), 'FAILED - Action: POD_Group_Assign_AppRole - app role assignment failed', if(not(equals(coalesce(outputs('POD_Group_Analyze_Results')?['podStatusCode'], 0), 200)), concat('FAILED - Action: POD_Group_Provision_On_Demand - HTTP ', string(coalesce(outputs('POD_Group_Analyze_Results')?['podStatusCode'], 0))), if(equals(coalesce(outputs('POD_Group_Analyze_Results')?['scimGroupFound'], false), false), 'FAILED - Action: POD_SCIM_Get_Group - group not found on SCIM endpoint', 'FAILED - Action: POD_Group_Verify_Provisioning_Logs - provisioning log not found'))))", - "summary": "@outputs('POD_Group_Analyze_Results')", - "errorDetails": { - "podResponse": "@if(not(equals(outputs('POD_Group_Provision_On_Demand')?['statusCode'], 200)), string(coalesce(body('POD_Group_Provision_On_Demand'), 'no response')), null)" - } - } + "name": "RestoreGroupFailedActionName", + "value": "@{if(greater(length(body('RestoreGroupTest_Filter_Phase3_Failed_Actions')), 0), first(body('RestoreGroupTest_Filter_Phase3_Failed_Actions'))?['name'], 'Unknown_Action')}" } - }, - "POD_Group_Cleanup_SCIM_Delete": { - "runAfter": { "Set_PODGroupTestOutputs": ["Succeeded", "Failed", "Skipped"] }, - "type": "Http", + }, + "RestoreGroupTest_Set_Phase3_Failed_Action_Response": { + "runAfter": { + "RestoreGroupTest_Set_Phase3_Failed_Action_Name": [ + "Succeeded" + ] + }, + "type": "SetVariable", "inputs": { - "method": "DELETE", - "uri": "@{concat(parameters('scimEndpoint'), '/Groups/', coalesce(first(body('POD_SCIM_Get_Group')?['Resources'])?['id'], 'not-found'))}", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } + "name": "RestoreGroupFailedActionResponse", + "value": "@if(greater(length(body('RestoreGroupTest_Filter_Phase3_Failed_Actions')), 0), coalesce(first(body('RestoreGroupTest_Filter_Phase3_Failed_Actions'))?['outputs'], json('{}')), json('{}'))" } - }, - "POD_Group_Cleanup_Remove_AppRole": { - "runAfter": { "POD_Group_Cleanup_SCIM_Delete": ["Succeeded", "Failed", "Skipped"] }, - "type": "Http", + }, + "RestoreGroupTest_Set_Phase3_Status_Failed": { + "runAfter": { + "RestoreGroupTest_Set_Phase3_Failed_Action_Response": [ + "Succeeded" + ] + }, + "type": "SetVariable", "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', coalesce(body('POD_Create_Graph_Group')?['id'], 'not-found'), '/appRoleAssignments/', coalesce(body('POD_Group_Assign_AppRole')?['id'], 'not-found'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } + "name": "RestoreGroupScopeExecutionStatus", + "value": "Failed" } + } }, - "POD_Group_Cleanup_Graph_Delete": { - "runAfter": { "POD_Group_Cleanup_Remove_AppRole": ["Succeeded", "Failed", "Skipped"] }, - "type": "Http", + "runAfter": { + "RestoreGroupTest_Phase3_Reassign": [ + "Failed" + ] + }, + "type": "Scope" + }, + "RestoreGroupTest_Set_Phase3_Status_Success": { + "runAfter": { + "RestoreGroupTest_Phase3_Reassign": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "RestoreGroupScopeExecutionStatus", + "value": "Succeeded" + } + }, + "RestoreGroupTest_Analyze_Phase3_Provisioning_Results": { + "runAfter": { + "RestoreGroupTest_Phase3_Reassign": [ + "Succeeded", + "Failed", + "TimedOut" + ], + "RestoreGroupTest_Capture_Phase3_Failed_Actions": [ + "Succeeded", + "Skipped" + ], + "RestoreGroupTest_Set_Phase3_Status_Success": [ + "Succeeded", + "Skipped" + ] + }, + "type": "Compose", + "inputs": { + "provisioningLogsCount": "@length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]')))", + "hasProvisioningLogs": "@greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0)", + "scimGroupRestored": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)", + "provisioningResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0), 'success', 'RestoreGroupTest_Get_Restore_Log: NO_LOGS_FOUND'), concat('Failed Action: ', variables('RestoreGroupFailedActionName')))", + "provisioningErrorDetails": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), null, variables('RestoreGroupFailedActionResponse'))", + "scimCheckResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0), 'success', 'RestoreGroupTest: Group not restored on SCIM server after reassign'), concat('Failed Action: ', variables('RestoreGroupFailedActionName')))", + "overallResult": "@if(equals(variables('RestoreGroupScopeExecutionStatus'), 'Succeeded'), if(and(greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0), greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)), 'PASSED', 'FAILED'), 'FAILED')" + } + }, + "RestoreGroupTest_Set_Phase3_Output_Variables": { + "actions": { + "RestoreGroupTest_Set_RestoreGroupTestOutputs_Phase3": { + "type": "SetVariable", "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', coalesce(body('POD_Create_Graph_Group')?['id'], 'not-found'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" + "name": "RestoreGroupTestOutputs", + "value": { + "result": "@if(and(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success')), 'success (UnassignReassign)', if(startsWith(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'Failed Action:'), concat('FAILED - [Reassign Phase] ', coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], '')), concat('FAILED - [Reassign Phase] Provisioning: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), ' | SCIM: ', coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''))))", + "overallResult": "@outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['overallResult']", + "provisioningLogsResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), 'success', 'failure')", + "scimCheckResult": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), 'success', 'failure')", + "errorDetails": { + "provisioningLogs": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningResult'], ''), 'success'), null, coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['provisioningErrorDetails'], 'Analyze step failed'))", + "scimCheck": "@if(equals(coalesce(outputs('RestoreGroupTest_Analyze_Phase3_Provisioning_Results')?['scimCheckResult'], ''), 'success'), null, 'Group not restored on SCIM server after reassign')" } + } } - } + } + }, + "runAfter": { + "RestoreGroupTest_Analyze_Phase3_Provisioning_Results": [ + "Succeeded", + "Failed" + ] + }, + "type": "Scope" + } + }, + "runAfter": { + "RestoreGroupTest_Set_Phase2_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} }, - "runAfter": {}, "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] - }, - { - "or": [ - { "equals": ["@triggerBody()?['EnabledTests']", "All"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "GroupTests"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "POD_Group_Test"] } - ] - } - ] + "and": [ + { + "equals": [ + "@toLower(variables('RestoreGroupTestOutputs')['overallResult'])", + "passed" + ] + } + ] }, "type": "If" + } + }, + "runAfter": { + "RestoreGroupTest_Set_Phase1_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "else": { + "actions": {} + }, + "expression": { + "and": [ + { + "equals": [ + "@toLower(variables('RestoreGroupTestOutputs')['overallResult'])", + "passed" + ] + } + ] + }, + "type": "If" + }, + "RestoreGroupTest_Cleanup_DeleteGroup": { + "runAfter": { + "RestoreGroupTest_Check_Phase1_Status": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] + }, + "type": "Http", + "inputs": { + "method": "DELETE", + "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'))}", + "authentication": { + "type": "ManagedServiceIdentity", + "audience": "https://graph.microsoft.com/" + } } -, - "Restore_Group_Test": { - "runAfter": {}, - "actions": { - "RestoreGroupTest_Actions": { - "type": "Scope", - "actions": { - "RestoreGroupTest_Phase1_Create_And_Assign": { - "type": "Scope", - "actions": { - "RestoreGroupTest_Create_Entra_Group": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "https://graph.microsoft.com/v1.0/groups", - "headers": { "Content-Type": "application/json" }, - "body": { - "displayName": "@{coalesce(triggerBody()?['initializationData']?['testGroupNames']?['restoreGroup'], concat('RestoreGrp-', substring(guid(),0,8)))}", - "mailEnabled": false, - "mailNickname": "@{concat('restoregrp', substring(guid(),0,8))}", - "securityEnabled": true, - "groupTypes": [] - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" + } } } }, - "RestoreGroupTest_Set_EntraGroupId": { - "runAfter": { "RestoreGroupTest_Create_Entra_Group": ["Succeeded"] }, - "type": "SetVariable", - "inputs": { - "name": "restoreEntraGroupId", - "value": "@{body('RestoreGroupTest_Create_Entra_Group')?['id']}" - } - }, - "RestoreGroupTest_Delay_Before_AppRole": { - "runAfter": { "RestoreGroupTest_Set_EntraGroupId": ["Succeeded"] }, - "type": "Wait", - "inputs": { - "interval": { - "count": 30, - "unit": "Second" - } - } + "else": { + "actions": {} }, - "RestoreGroupTest_Assign_AppRole": { - "runAfter": { "RestoreGroupTest_Delay_Before_AppRole": ["Succeeded"] }, - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'), '/appRoleAssignments')}", - "headers": { "Content-Type": "application/json" }, - "body": { - "principalId": "@{variables('restoreEntraGroupId')}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" + "expression": { + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreGroupTest_Set_AppRoleAssignmentId": { - "runAfter": { "RestoreGroupTest_Assign_AppRole": ["Succeeded"] }, - "type": "SetVariable", - "inputs": { - "name": "restoreGroupAppRoleAssignmentId", - "value": "@{body('RestoreGroupTest_Assign_AppRole')?['id']}" - } - }, - "RestoreGroupTest_DoUntil_Verify_SCIM_Create": { - "runAfter": { "RestoreGroupTest_Set_AppRoleAssignmentId": ["Succeeded"] }, - "type": "Until", - "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0)", - "limit": { "count": 200, "timeout": "PT50M" }, - "actions": { - "RestoreGroupTest_SCIM_Check_Created": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "GroupTests" + ] }, - "RestoreGroupTest_Delay_Create_15s": { - "runAfter": { "RestoreGroupTest_SCIM_Check_Created": ["Succeeded", "Failed"] }, - "type": "Wait", - "inputs": { "interval": { "count": 15, "unit": "Second" } } + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Restore_Group_Test" + ] } - } + ] }, - "RestoreGroupTest_Get_Create_Log": { - "runAfter": { "RestoreGroupTest_DoUntil_Verify_SCIM_Create": ["Succeeded", "Failed"] }, - "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', variables('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { "Accept": "application/json" }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } + "type": "If" } }, - "RestoreGroupTest_Phase2_Unassign": { - "runAfter": { "RestoreGroupTest_Phase1_Create_And_Assign": ["Succeeded"] }, - "type": "Scope", - "actions": { - "RestoreGroupTest_Unassign_AppRole": { - "type": "Http", - "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'), '/appRoleAssignments/', variables('restoreGroupAppRoleAssignmentId'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreGroupTest_DoUntil_Verify_SCIM_Gone": { - "runAfter": { "RestoreGroupTest_Unassign_AppRole": ["Succeeded"] }, - "type": "Until", - "expression": "@equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[\"placeholder\"]'))), 0)", - "limit": { "count": 200, "timeout": "PT50M" }, - "actions": { - "RestoreGroupTest_SCIM_Check_Gone": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreGroupTest_Delay_Gone_15s": { - "runAfter": { "RestoreGroupTest_SCIM_Check_Gone": ["Succeeded", "Failed"] }, - "type": "Wait", - "inputs": { "interval": { "count": 15, "unit": "Second" } } - } - } - }, - "RestoreGroupTest_Get_Delete_Log": { - "runAfter": { "RestoreGroupTest_DoUntil_Verify_SCIM_Gone": ["Succeeded", "Failed"] }, - "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', variables('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Delete%27')}", - "method": "GET", - "headers": { "Accept": "application/json" }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - } + "runAfter": { + "Response_Immediate": [ + "Succeeded" + ], + "Initialize_PODGroupTestOutputs": [ + "Succeeded" + ], + "Initialize_RestoreGroupScopeExecutionStatus": [ + "Succeeded" + ] }, - "RestoreGroupTest_Phase3_Reassign": { - "runAfter": { "RestoreGroupTest_Phase2_Unassign": ["Succeeded"] }, - "type": "Scope", - "actions": { - "RestoreGroupTest_Reassign_AppRole": { - "type": "Http", - "inputs": { - "method": "POST", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'), '/appRoleAssignments')}", - "headers": { "Content-Type": "application/json" }, - "body": { - "principalId": "@{variables('restoreEntraGroupId')}", - "resourceId": "@{parameters('servicePrincipalId')}", - "appRoleId": "@{coalesce(first(triggerBody()?['initializationData']?['userAppRole'])?['id'], first(triggerBody()?['initializationData']?['appRoles']?['appRoles'])?['id'])}" - }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, - "RestoreGroupTest_DoUntil_Verify_SCIM_Restored": { - "runAfter": { "RestoreGroupTest_Reassign_AppRole": ["Succeeded"] }, - "type": "Until", - "expression": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)", - "limit": { "count": 200, "timeout": "PT50M" }, - "actions": { - "RestoreGroupTest_SCIM_Check_Restored": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Groups?filter=displayName%20eq%20%22', encodeUriComponent(body('RestoreGroupTest_Create_Entra_Group')?['displayName']), '%22')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, - "RestoreGroupTest_Delay_Restored_15s": { - "runAfter": { "RestoreGroupTest_SCIM_Check_Restored": ["Succeeded", "Failed"] }, - "type": "Wait", - "inputs": { "interval": { "count": 15, "unit": "Second" } } - } - } - }, - "RestoreGroupTest_Get_Restore_Log": { - "runAfter": { "RestoreGroupTest_DoUntil_Verify_SCIM_Restored": ["Succeeded", "Failed"] }, - "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', variables('restoreEntraGroupId'), '%27%20and%20provisioningAction%20eq%20%27Create%27')}", - "method": "GET", - "headers": { "Accept": "application/json" }, - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - } + "type": "Scope" + }, + "Compose_Final_Results": { + "runAfter": { + "GroupTests_Scope": [ + "Succeeded", + "Failed", + "Skipped", + "TimedOut" + ] }, - "RestoreGroupTest_Compose_Summary": { - "runAfter": { - "RestoreGroupTest_Phase3_Reassign": ["Succeeded", "Failed", "Skipped"] - }, - "type": "Compose", - "inputs": { - "testName": "Restore_Group_Test", - "timestamp": "@utcNow()", - "entraGroupId": "@variables('restoreEntraGroupId')", - "phase1_createLogFound": "@greater(length(coalesce(body('RestoreGroupTest_Get_Create_Log')?['value'], json('[]'))), 0)", - "phase1_scimGroupFound": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0)", - "phase2_deleteLogFound": "@greater(length(coalesce(body('RestoreGroupTest_Get_Delete_Log')?['value'], json('[]'))), 0)", - "phase2_scimGroupGone": "@equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)", - "phase3_restoreLogFound": "@greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0)", - "phase3_scimGroupRestored": "@greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)", - "overallResult": "@if(and(and(and(greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Created')?['Resources'], json('[]'))), 0), equals(length(coalesce(body('RestoreGroupTest_SCIM_Check_Gone')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('RestoreGroupTest_SCIM_Check_Restored')?['Resources'], json('[]'))), 0)), greater(length(coalesce(body('RestoreGroupTest_Get_Restore_Log')?['value'], json('[]'))), 0)), 'PASSED', 'FAILED')" - } - } - } - }, - "RestoreGroupTest_Set_Outputs": { - "runAfter": { "RestoreGroupTest_Actions": ["Succeeded", "Failed", "Skipped"] }, - "type": "SetVariable", - "inputs": { - "name": "RestoreGroupTestOutputs", - "value": { - "overallResult": "@coalesce(outputs('RestoreGroupTest_Compose_Summary')?['overallResult'], 'FAILED')", - "result": "@if(equals(coalesce(outputs('RestoreGroupTest_Compose_Summary')?['overallResult'], 'FAILED'), 'PASSED'), 'success (UnassignReassign)', if(equals(coalesce(outputs('RestoreGroupTest_Compose_Summary')?['phase1_scimGroupFound'], false), false), 'FAILED - [Create Phase] Action: RestoreGroupTest_SCIM_Check_Created - group not found on SCIM endpoint after provisioning', if(equals(coalesce(outputs('RestoreGroupTest_Compose_Summary')?['phase2_scimGroupGone'], false), false), 'FAILED - [Unassign Phase] Action: RestoreGroupTest_SCIM_Check_Gone - group still present on SCIM endpoint after unassign', 'FAILED - [Reassign Phase] Action: RestoreGroupTest_SCIM_Check_Restored - group not restored on SCIM endpoint after reassign')))", - "summary": "@outputs('RestoreGroupTest_Compose_Summary')", - "detailedResults": [], - "errorDetails": { - "summary": "@if(equals(coalesce(outputs('RestoreGroupTest_Compose_Summary')?['overallResult'], 'FAILED'), 'PASSED'), null, string(outputs('RestoreGroupTest_Compose_Summary')))" - } + "type": "Compose", + "inputs": { + "CreateGroupTestOutputs": "@variables('CreateGroupTestOutputs')", + "UpdateGroupTestOutputs": "@variables('UpdateGroupTestOutputs')", + "DeleteGroupTestOutputs": "@variables('DeleteGroupTestOutputs')", + "AddMemberTestOutputs": "@variables('AddMemberTestOutputs')", + "RemoveMemberTestOutputs": "@variables('RemoveMemberTestOutputs')", + "PODGroupTestOutputs": "@variables('PODGroupTestOutputs')", + "RestoreGroupTestOutputs": "@variables('RestoreGroupTestOutputs')", + "testSuiteName": "GroupTests", + "status": "@if(or(or(or(or(or(equals(variables('CreateGroupTestOutputs')['overallResult'], 'FAILED'), equals(variables('UpdateGroupTestOutputs')['overallResult'], 'FAILED')), equals(variables('DeleteGroupTestOutputs')['overallResult'], 'FAILED')), equals(variables('AddMemberTestOutputs')['overallResult'], 'FAILED')), equals(variables('RemoveMemberTestOutputs')['overallResult'], 'FAILED')), equals(variables('PODGroupTestOutputs')['overallResult'], 'FAILED')), 'Failed', 'Passed')", + "workflowRunId": "@workflow().run.name" } } }, - "RestoreGroupTest_Cleanup": { - "runAfter": { "RestoreGroupTest_Set_Outputs": ["Succeeded", "Failed", "Skipped"] }, - "type": "Http", - "inputs": { - "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/groups/', variables('restoreEntraGroupId'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - } - }, - "else": { "actions": {} }, - "expression": { - "and": [ - { - "equals": [ - "@triggerBody()?['initializationData']?['isGroupSupported']", - true - ] - }, - { - "or": [ - { "equals": ["@triggerBody()?['EnabledTests']", "All"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "GroupTests"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "Restore_Group_Test"] } - ] - } - ] + "outputs": {} }, - "type": "If" -} - }, - "runAfter": { - "Response_Immediate": [ - "Succeeded" - ], - "Initialize_PODGroupTestOutputs": [ - "Succeeded" - ], - "Initialize_RestoreGroupTestVariables": [ - "Succeeded" - ] - }, - "type": "Scope" - }, - "Compose_Final_Results": { - "runAfter": { - "GroupTests_Scope": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] - }, - "type": "Compose", - "inputs": { - "CreateGroupTestOutputs": "@variables('CreateGroupTestOutputs')", - "UpdateGroupTestOutputs": "@variables('UpdateGroupTestOutputs')", - "DeleteGroupTestOutputs": "@variables('DeleteGroupTestOutputs')", - "AddMemberTestOutputs": "@variables('AddMemberTestOutputs')", - "RemoveMemberTestOutputs": "@variables('RemoveMemberTestOutputs')", - "PODGroupTestOutputs": "@variables('PODGroupTestOutputs')", - "RestoreGroupTestOutputs": "@variables('RestoreGroupTestOutputs')", - "testSuiteName": "GroupTests", - "status": "@if(or(or(or(or(or(equals(variables('CreateGroupTestOutputs')['overallResult'], 'FAILED'), equals(variables('UpdateGroupTestOutputs')['overallResult'], 'FAILED')), equals(variables('DeleteGroupTestOutputs')['overallResult'], 'FAILED')), equals(variables('AddMemberTestOutputs')['overallResult'], 'FAILED')), equals(variables('RemoveMemberTestOutputs')['overallResult'], 'FAILED')), equals(variables('PODGroupTestOutputs')['overallResult'], 'FAILED')), 'Failed', 'Passed')", - "workflowRunId": "@workflow().run.name" - } - } - }, - "outputs": {} - }, - "kind": "Stateful" + "kind": "Stateful" } \ No newline at end of file diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json index 3a7ce7a..e4661de 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Initialization_Workflow.json @@ -875,6 +875,23 @@ "type": "Compose", "inputs": "@if(greater(length(body('Filter_Group_ObjectMapping')), 0), contains(toLower(coalesce(string(first(body('Filter_Group_ObjectMapping'))?['flowTypes']), '')), 'delete'), false)" }, + "Compose_IsVerbosePatchSupported": { + "runAfter": { + "Filter_PatchBodyFormat_Setting": [ + "Succeeded" + ] + }, + "type": "Compose", + "inputs": "@greater(length(body('Filter_PatchBodyFormat_Setting')), 0)" + }, + "Filter_PatchBodyFormat_Setting": { + "runAfter": {}, + "type": "Query", + "inputs": { + "from": "@coalesce(body('Get_Job_Details')?['synchronizationJobSettings'], json('[]'))", + "where": "@equals(toLower(item()?['name']), 'patchbodyformat')" + } + }, "Compose_UpdateTest_ExcludeList": { "runAfter": { "Select_ObjectAddOnly_SourceNames": [ @@ -1188,7 +1205,8 @@ "schemas": "@createArray('urn:ietf:params:scim:schemas:core:2.0:Group')", "displayName": "placeholder-group", "externalId": "placeholder-extid" - } + }, + "isVerbosePatchSupported": "@outputs('Compose_IsVerbosePatchSupported')" } } } diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json index 7b0df36..97556e9 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Parameters.json @@ -23,24 +23,16 @@ "SCIM_Group_Create_Test", "SCIM_User_Update_Test", "SCIM_Group_Update_Test", - "SCIM_User_Delete_Test", - "SCIM_Group_Delete_Test", "SCIM_User_Pagination_Test", "SCIM_Group_Pagination_Test", "POD_User_Test", "POD_Group_Test", - "Restore_User_Test" + "Restore_User_Test", + "Restore_Group_Test" ], "type": "String", "value": "All" }, - "IsSoftDeleted": { - "metadata": { - "description": "If true, user will be disabled (soft delete). If false, user will be deleted (hard delete)." - }, - "type": "Bool", - "value": true - }, "LogicAppVersion": { "metadata": { "description": "Version of the Logic App for tracking and deployment purposes" @@ -234,7 +226,7 @@ }, "scimEndpoint": { "type": "String", - "value": "https://scimserver2.yellowsmoke-af7a3fff.eastus.azurecontainerapps.io/scim/endpoints/f4c95b63-db4c-4f0a-91b0-78fdf88007be" + "value": "https://scimserver2.yellowsmoke-af7a3fff.eastus.azurecontainerapps.io/scim/endpoints/8f3c4c4a-7b5d-4e91-9a62-2d7f1b8e5c73" }, "scimOAuthScope": { "metadata": { @@ -251,61 +243,61 @@ "value": "https://scimserver2.yellowsmoke-af7a3fff.eastus.azurecontainerapps.io/scim/oauth/token" }, "federatedEntraTenantId": { - "metadata": { - "description": "Entra tenant ID used to construct the token endpoint URL for federated identity token acquisition." - }, - "type": "String", - "value": "changeme-tenant-id" + "metadata": { + "description": "Entra tenant ID used to construct the token endpoint URL for federated identity token acquisition." }, - "federatedApplicationId": { - "metadata": { - "description": "OAuth2 Application ID for the Entra app registration used in federated identity token acquisition." - }, - "type": "String", - "value": "changeme-client-id" + "type": "String", + "value": "changeme-tenant-id" + }, + "federatedApplicationId": { + "metadata": { + "description": "OAuth2 Application ID for the Entra app registration used in federated identity token acquisition." }, - "federatedApplicationClientSecret": { - "metadata": { - "description": "OAuth2 Client Secret for the Entra app registration used in federated identity token acquisition." - }, - "type": "String", - "value": "changeme-client-secret" + "type": "String", + "value": "changeme-client-id" + }, + "federatedApplicationClientSecret": { + "metadata": { + "description": "OAuth2 Client Secret for the Entra app registration used in federated identity token acquisition." }, - "federatedTokenEndpoint": { - "metadata": { - "description": "ISV token endpoint URL for federated identity token exchange." - }, - "type": "String", - "value": "changeme-federated-endpoint" + "type": "String", + "value": "changeme-client-secret" + }, + "federatedTokenEndpoint": { + "metadata": { + "description": "ISV token endpoint URL for federated identity token exchange." }, - "federatedClientId": { - "metadata": { - "description": "ISV client ID used in federated identity token exchange." - }, - "type": "String", - "value": "changeme-federated-app-id" + "type": "String", + "value": "changeme-federated-endpoint" + }, + "federatedClientId": { + "metadata": { + "description": "ISV client ID used in federated identity token exchange." }, - "federatedBaseAddress": { - "metadata": { - "description": "ISV base address sent in the federated identity token exchange request." - }, - "type": "String", - "value": "changeme-base-address" + "type": "String", + "value": "changeme-federated-app-id" + }, + "federatedBaseAddress": { + "metadata": { + "description": "ISV base address sent in the federated identity token exchange request." }, - "federatedAudience": { - "metadata": { - "description": "Audience value for federated token exchange." - }, - "type": "String", - "value": "changeme-audience" + "type": "String", + "value": "changeme-base-address" + }, + "federatedAudience": { + "metadata": { + "description": "Audience value for federated token exchange." }, + "type": "String", + "value": "changeme-audience" + }, "servicePrincipalId": { "type": "String", - "value": "3197053d-46d9-4a22-b843-6a9d98ec267e" + "value": "ed1a4e07-8807-4362-8b9b-c8edc2b51747" }, "testUserDomain": { "type": "String", - "value": "proviamtest07.onmicrosoft.com" + "value": "proviamla01.onmicrosoft.com" }, "scimTargetUserValues": { "type": "Array", diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Workflow.json index bcec026..d7745e7 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/Orchestrator_Workflow.json @@ -110,7 +110,7 @@ "body": { "initializationData": "@body('Call_Initialization_Workflow')", "EnabledTests": "@parameters('EnabledTests')", - "IsSoftDeleted": "@and(coalesce(body('Call_Initialization_Workflow')?['isSoftDeleteSupported'], true), parameters('IsSoftDeleted'))" + "IsSoftDeleted": "@coalesce(body('Call_Initialization_Workflow')?['isSoftDeleteSupported'], true)" } } }, @@ -228,7 +228,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'UserTests_Workflow/runs/', coalesce(actions('Call_UserTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_UserTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'UserTests_Workflow/runs/', coalesce(actions('Call_UserTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_UserTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -269,7 +269,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'GroupTests_Workflow/runs/', coalesce(actions('Call_GroupTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_GroupTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'GroupTests_Workflow/runs/', coalesce(actions('Call_GroupTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_GroupTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -310,7 +310,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'SCIMTests_Workflow/runs/', coalesce(actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'SCIMTests_Workflow/runs/', coalesce(actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -330,7 +330,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'UserTests_Workflow/runs/', coalesce(actions('Call_UserTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_UserTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'UserTests_Workflow/runs/', coalesce(actions('Call_UserTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_UserTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -360,7 +360,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'GroupTests_Workflow/runs/', coalesce(actions('Call_GroupTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_GroupTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'GroupTests_Workflow/runs/', coalesce(actions('Call_GroupTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_GroupTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -390,7 +390,7 @@ "type": "Http", "inputs": { "method": "GET", - "uri": "@concat(outputs('Compose_ManagementApiBase'), 'SCIMTests_Workflow/runs/', coalesce(actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2024-04-01')", + "uri": "@concat(outputs('Compose_ManagementApiBase'), 'SCIMTests_Workflow/runs/', coalesce(actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['workflowRunId'], actions('Call_SCIMTests_Workflow')?['outputs']?['body']?['RunId'], 'UNKNOWN'), '/actions/Compose_Final_Results?api-version=2022-03-01')", "authentication": { "type": "ManagedServiceIdentity", "audience": "https://management.azure.com/" @@ -440,29 +440,29 @@ "inputs": { "enabledTestsEvaluation": { "createUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Create_User_Test'))", - "createGroupTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Create_Group_Test')))", + "createGroupTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Create_Group_Test'))", "updateUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Update_User_Test'))", - "disableUserTestEnabled": "@and(coalesce(body('Call_Initialization_Workflow')?['isSoftDeleteSupported'], true), parameters('IsSoftDeleted'), or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Disable_User_Test')))", + "disableUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Disable_User_Test'))", "deleteUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Delete_User_Test'))", "managerTestEnabled": "@and(body('Call_Initialization_Workflow')?['isManagerAttributeSupported'], or(or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests')), equals(parameters('EnabledTests'), 'User_Update_Manager_Test')))", "restoreUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'Restore_User_Test'))", "podUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'UserTests'), equals(parameters('EnabledTests'), 'POD_User_Test'))", - "podGroupTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'POD_Group_Test')))", - "restoreGroupTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Restore_Group_Test')))", - "updateGroupTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Update_Group_Test')))", - "deleteGroupTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Delete_Group_Test')))", - "addMemberTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests')), equals(parameters('EnabledTests'), 'Group_Update_Add_Member_Test')))", - "removeMemberTestEnabled": "@and(body('Call_Initialization_Workflow')?['isGroupSupported'], or(or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests')), equals(parameters('EnabledTests'), 'Group_Update_Remove_Member_Test')))", + "podGroupTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'POD_Group_Test'))", + "restoreGroupTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Restore_Group_Test'))", + "updateGroupTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Update_Group_Test'))", + "deleteGroupTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Delete_Group_Test'))", + "addMemberTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Group_Update_Add_Member_Test'))", + "removeMemberTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'GroupTests'), equals(parameters('EnabledTests'), 'Group_Update_Remove_Member_Test'))", "schemaDiscoverabilityTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'Schema_Discoverability_Test'))", "nullUpdateUserTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Null_Update_Test'), equals(parameters('EnabledTests'), 'Null_Update_User_Test'))", "scimUserCreateTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_User_Create_Test'))", - "scimGroupCreateTestEnabled": "@and(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false), or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Create_Test')))", + "scimGroupCreateTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Create_Test'))", "scimUserUpdateTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_User_Update_Test'))", - "scimGroupUpdateTestEnabled": "@and(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false), or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Update_Test')))", + "scimGroupUpdateTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Update_Test'))", "validateCredentialsTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'Validate_Credentials_Test'))", "federatedIdentityTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'Federated_Identity_Test'))", "scimUserPaginationTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_User_Pagination_Test'))", - "scimGroupPaginationTestEnabled": "@and(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false), or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Pagination_Test')))" + "scimGroupPaginationTestEnabled": "@or(equals(parameters('EnabledTests'), 'All'), equals(parameters('EnabledTests'), 'SCIMTests'), equals(parameters('EnabledTests'), 'SCIM_Group_Pagination_Test'))" }, "testResults": { "createUserTestResult": "@coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['overallResult'], 'SKIPPED')", @@ -502,7 +502,9 @@ "GroupTests_Workflow": "@outputs('Compose_GroupTests_RunLink')", "SCIMTests_Workflow": "@outputs('Compose_SCIMTests_RunLink')" }, - "overallLogicAppResult": "@if(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(equals(coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), equals(coalesce(body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), or(equals(coalesce(body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'))), equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(actions('Call_Initialization_Workflow')?['status'], 'Failed')), equals(coalesce(body('Poll_UserTests_Status')?['properties']?['status'], ''), 'Failed')), equals(coalesce(body('Poll_GroupTests_Status')?['properties']?['status'], ''), 'Failed')), 'Failed', if(equals(coalesce(body('Poll_SCIMTests_Status')?['properties']?['status'], ''), 'Failed'), 'Failed', 'Succeeded'))" + "overallLogicAppResult": "@if(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(or(equals(coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['overallResult'],'SKIPPED'),'FAILED'),equals(coalesce(body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),or(equals(coalesce(body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['overallResult'],'SKIPPED'),'FAILED'),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['overallResult'],'SKIPPED'),'FAILED'))),equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['overallResult'],'SKIPPED'),'FAILED')),equals(actions('Call_Initialization_Workflow')?['status'],'Failed')),equals(coalesce(body('Poll_UserTests_Status')?['properties']?['status'],''),'Failed')),equals(coalesce(body('Poll_GroupTests_Status')?['properties']?['status'],''),'Failed')),and(not(equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'],'SKIPPED'),'PASSED')),not(equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'],'SKIPPED'),'PASSED')))),not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'],false))),'Failed',if(equals(coalesce(body('Poll_SCIMTests_Status')?['properties']?['status'],''),'Failed'),'Failed','Succeeded'))", + "optionalTestsResult": "@if(or(or(or(equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED')), 'WARNING', 'PASSED')", + "authenticationModelResult": "@if(or(equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'], 'SKIPPED'), 'PASSED'), equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'], 'SKIPPED'), 'PASSED')), 'PASSED', 'FAILED')" } }, "Final_TestResults": { @@ -516,6 +518,7 @@ "testResults": [ { "testName": "Create_User_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['CreateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -524,6 +527,7 @@ }, { "testName": "Update_User_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['UpdateUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -532,7 +536,8 @@ }, { "testName": "Disable_User_Test", - "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isSoftDeleteSupported'], true)), 'SKIPPED - No active attribute mapping found', if(not(parameters('IsSoftDeleted')), 'SKIPPED - IsSoftDeleted parameter is false', 'SKIPPED')))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DisableUserTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_UserTests_RunLink'))", @@ -540,7 +545,8 @@ }, { "testName": "Delete_User_Test", - "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isUserDeleteSupported'], true)), 'SKIPPED - Delete action not enabled on target object', 'SKIPPED'))", + "testCategory": "optional", + "testResult": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), replace(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['result'], 'SKIPPED'), 'FAILED', 'WARNING'), coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['result'], 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['DeleteUserTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_UserTests_RunLink'))", @@ -548,6 +554,7 @@ }, { "testName": "User_Update_Manager_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isManagerAttributeSupported']), 'SKIPPED - IsManagerAttributeSupported is false', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['ManagerTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -556,6 +563,7 @@ }, { "testName": "Restore_User_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['RestoreUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -564,7 +572,8 @@ }, { "testName": "Create_Group_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isGroupSupported']), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['CreateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -572,7 +581,8 @@ }, { "testName": "Update_Group_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isGroupSupported']), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['UpdateGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -580,7 +590,8 @@ }, { "testName": "Delete_Group_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isGroupSupported']), 'SKIPPED - IsGroupSupported is false', if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupDeleteSupported'], true)), 'SKIPPED - Delete action not enabled on target object', 'SKIPPED')))", + "testCategory": "optional", + "testResult": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), replace(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['result'], 'SKIPPED'), 'FAILED', 'WARNING'), coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['result'], 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['DeleteGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -588,7 +599,8 @@ }, { "testName": "Group_Update_Add_Member_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isGroupSupported']), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['AddMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -596,7 +608,8 @@ }, { "testName": "Group_Update_Remove_Member_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['result'], if(not(body('Call_Initialization_Workflow')?['isGroupSupported']), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RemoveMemberTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -604,6 +617,7 @@ }, { "testName": "Schema_Discoverability_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SchemaDiscoverabilityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -612,6 +626,7 @@ }, { "testName": "SCIM_Null_Update_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMNullUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -620,6 +635,7 @@ }, { "testName": "SCIM_User_Create_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -628,7 +644,8 @@ }, { "testName": "SCIM_Group_Create_Test", - "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupCreateTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_SCIMTests_RunLink'))", @@ -636,6 +653,7 @@ }, { "testName": "SCIM_User_Update_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -644,7 +662,8 @@ }, { "testName": "SCIM_Group_Update_Test", - "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupUpdateTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_SCIMTests_RunLink'))", @@ -652,6 +671,7 @@ }, { "testName": "Validate_Credentials_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['ValidateCredentialsTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -660,6 +680,7 @@ }, { "testName": "Federated_Identity_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['FederatedIdentityTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -668,6 +689,7 @@ }, { "testName": "SCIM_User_Pagination_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMUserPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -676,7 +698,8 @@ }, { "testName": "SCIM_Group_Pagination_Test", - "testResult": "@coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "optional", + "testResult": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), replace(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['result'], 'SKIPPED'), 'FAILED', 'WARNING'), coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['result'], 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_SCIMTests_Output_Content')?['SCIMGroupPaginationTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_SCIMTests_RunLink'))", @@ -684,6 +707,7 @@ }, { "testName": "POD_User_Test", + "testCategory": "mandatory", "testResult": "@coalesce(body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['result'], 'SKIPPED')", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_UserTests_Output_Content')?['PODUserTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", @@ -692,7 +716,8 @@ }, { "testName": "POD_Group_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "mandatory", + "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'FAILED - [Initialization Phase] Schema_Discovery - /Schemas did not return Group schema (urn:ietf:params:scim:schemas:core:2.0:Group). Group provisioning is mandatory.', 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['PODGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -700,7 +725,8 @@ }, { "testName": "Restore_Group_Test", - "testResult": "@coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['result'], if(not(coalesce(body('Call_Initialization_Workflow')?['isGroupSupported'], false)), 'SKIPPED - IsGroupSupported is false', 'SKIPPED'))", + "testCategory": "optional", + "testResult": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), replace(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['result'], 'SKIPPED'), 'FAILED', 'WARNING'), coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['result'], 'SKIPPED'))", "provisioningErrorDetails": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['errorDetails'], '')", "recommendationUrl": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'FAILED'), 'https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/known-issues', '')", "runLink": "@if(equals(coalesce(body('Get_GroupTests_Output_Content')?['RestoreGroupTestOutputs']?['overallResult'], 'SKIPPED'), 'SKIPPED'), '', outputs('Compose_GroupTests_RunLink'))", @@ -731,4 +757,4 @@ "outputs": {} }, "kind": "Stateful" -} \ No newline at end of file +} diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SCIMTests_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SCIMTests_Workflow.json index 9d56edc..756988d 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SCIMTests_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/SCIMTests_Workflow.json @@ -138,7 +138,7 @@ "summary": null, "detailedResults": [], "errorDetails": { - "summary": null + "summary": null } } }, @@ -1320,7 +1320,7 @@ "type": "Query", "inputs": { "from": "@coalesce(triggerBody()?['initializationData']?['sourceToTargetLookup'], json('[]'))", - "where": "@and(equals(coalesce(item()?['matchingPrecedence'], 0), 0), not(equals(coalesce(item()?['source'], ''), 'manager')), not(equals(coalesce(item()?['target'], ''), 'userName')), not(equals(coalesce(item()?['target'], ''), 'active')), not(equals(coalesce(item()?['target'], ''), 'externalId')), not(equals(coalesce(item()?['target'], ''), 'roles')), if(startsWith(coalesce(item()?['target'], ''), 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:'), not(contains(string(body('Query_Required_Enterprise_Attrs')), concat('\"name\":\"', last(split(item()?['target'], ':')), '\"'))), not(contains(string(body('Query_Required_Core_Attrs')), concat('\"name\":\"', if(contains(coalesce(item()?['target'], ''), '['), first(split(item()?['target'], '[')), if(contains(coalesce(item()?['target'], ''), '.'), first(split(item()?['target'], '.')), coalesce(item()?['target'], ''))), '\"')))))" + "where": "@and(equals(coalesce(item()?['matchingPrecedence'], 0), 0), not(contains(toLower(coalesce(item()?['targetSchema']?['type'], '')), 'reference')), not(equals(coalesce(item()?['target'], ''), 'userName')), not(equals(coalesce(item()?['target'], ''), 'active')), not(equals(coalesce(item()?['target'], ''), 'externalId')), not(equals(coalesce(item()?['target'], ''), 'roles')), if(startsWith(coalesce(item()?['target'], ''), 'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:'), not(contains(string(body('Query_Required_Enterprise_Attrs')), concat('\"name\":\"', last(split(item()?['target'], ':')), '\"'))), not(contains(string(body('Query_Required_Core_Attrs')), concat('\"name\":\"', if(contains(coalesce(item()?['target'], ''), '['), first(split(item()?['target'], '[')), if(contains(coalesce(item()?['target'], ''), '.'), first(split(item()?['target'], '.')), coalesce(item()?['target'], ''))), '\"')))))" } }, "ForEach_Build_Null_Patch_Operations": { @@ -1359,18 +1359,69 @@ } } }, - "Compose_SCIM_Null_Patch_Body": { + "Select_Null_NonVerbose_KeyValues": { + "runAfter": { + "ForEach_Build_Null_Patch_Operations": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Null_Update_Target_Attributes')", + "select": "@concat('\"', replace(item()?['target'], '\"', '\\\"'), '\":\"\"')" + } + }, + "Filter_Null_MultiValued_Ops": { + "runAfter": { + "ForEach_Build_Null_Patch_Operations": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@variables('scimNullPatchOperations')", + "where": "@contains(coalesce(item()?['path'], ''), '[')" + } + }, + "Filter_Null_Scalar_Attrs": { "runAfter": { "ForEach_Build_Null_Patch_Operations": [ "Succeeded" ] }, + "type": "Query", + "inputs": { + "from": "@body('Filter_Null_Update_Target_Attributes')", + "where": "@not(contains(coalesce(item()?['target'], ''), '['))" + } + }, + "Select_Null_Batch_KeyValues": { + "runAfter": { + "Filter_Null_Scalar_Attrs": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Null_Scalar_Attrs')", + "select": "@concat('\"', replace(item()?['target'], '\"', '\\\"'), '\":\"\"')" + } + }, + "Compose_SCIM_Null_Patch_Body": { + "runAfter": { + "Select_Null_Batch_KeyValues": [ + "Succeeded" + ], + "Filter_Null_MultiValued_Ops": [ + "Succeeded" + ] + }, "type": "Compose", "inputs": { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ], - "Operations": "@variables('scimNullPatchOperations')" + "Operations": "@if(coalesce(triggerBody()?['initializationData']?['isVerbosePatchSupported'], true), variables('scimNullPatchOperations'), union(body('Filter_Null_MultiValued_Ops'), if(greater(length(body('Filter_Null_Scalar_Attrs')), 0), json(concat('[{\"op\":\"replace\",\"value\":{', join(body('Select_Null_Batch_KeyValues'), ','), '}}]')), json('[]'))))" } }, "SCIM_Patch_Null_Update": { @@ -1497,18 +1548,69 @@ } } }, - "Compose_Null_Patch_Request_Phase2": { + "Select_Null_Phase2_NonVerbose_KeyValues": { + "runAfter": { + "Select_Null_Patch_Ops_Phase2": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Null_Update_Target_Attributes')", + "select": "@concat('\"', replace(item()?['target'], '\"', '\\\"'), '\":null')" + } + }, + "Filter_Null_Phase2_MultiValued_Ops": { + "runAfter": { + "Select_Null_Patch_Ops_Phase2": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@body('Select_Null_Patch_Ops_Phase2')", + "where": "@contains(coalesce(item()?['path'], ''), '[')" + } + }, + "Filter_Null_Phase2_Batch_Ops": { "runAfter": { "Select_Null_Patch_Ops_Phase2": [ "Succeeded" ] }, + "type": "Query", + "inputs": { + "from": "@body('Select_Null_Patch_Ops_Phase2')", + "where": "@not(contains(coalesce(item()?['path'], ''), '['))" + } + }, + "Select_Null_Phase2_Batch_KV": { + "runAfter": { + "Filter_Null_Phase2_Batch_Ops": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Null_Phase2_Batch_Ops')", + "select": "@concat('\"', replace(item()?['path'], '\"', '\\\"'), '\":null')" + } + }, + "Compose_Null_Patch_Request_Phase2": { + "runAfter": { + "Select_Null_Phase2_Batch_KV": [ + "Succeeded" + ], + "Filter_Null_Phase2_MultiValued_Ops": [ + "Succeeded" + ] + }, "type": "Compose", "inputs": { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ], - "Operations": "@body('Select_Null_Patch_Ops_Phase2')" + "Operations": "@if(coalesce(triggerBody()?['initializationData']?['isVerbosePatchSupported'], true), body('Select_Null_Patch_Ops_Phase2'), union(body('Filter_Null_Phase2_MultiValued_Ops'), if(greater(length(body('Filter_Null_Phase2_Batch_Ops')), 0), json(concat('[{\"op\":\"replace\",\"value\":{', join(body('Select_Null_Phase2_Batch_KV'), ','), '}}]')), json('[]'))))" } }, "SCIM_Patch_Null_Update_Phase2": { @@ -1621,23 +1723,6 @@ "overallResult": "@if(and(not(empty(outputs('Compose_SCIM_Null_User_Id'))), or(equals(outputs('SCIM_Patch_Null_Update')['statusCode'], 200), equals(outputs('SCIM_Patch_Null_Update')['statusCode'], 204)), equals(length(body('Filter_Failed_Null_Verifications')), 0), or(equals(outputs('SCIM_Patch_Null_Update_Phase2')['statusCode'], 200), equals(outputs('SCIM_Patch_Null_Update_Phase2')['statusCode'], 204)), equals(length(body('Filter_Failed_Null_Phase2_Verifications')), 0)), 'PASSED', 'FAILED')" } }, - "SCIM_Delete_Null_Test_User": { - "runAfter": { - "Compose_SCIM_Null_Update_Summary": [ - "Succeeded", - "Failed" - ] - }, - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users/', outputs('Compose_SCIM_Null_User_Id'))}", - "method": "DELETE", - "headers": { - "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", - "Accept": "@{parameters('scimContentType')}" - } - } - }, "Filter_MultiValue_Targets": { "type": "Query", "inputs": { @@ -1846,6 +1931,44 @@ ] }, "type": "Scope" + }, + "SCIM_Null_Update_Cleanup": { + "runAfter": { + "Set_SCIMNullUpdate_Output_Variables": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "not": { + "equals": [ + "@coalesce(outputs('Compose_SCIM_Null_User_Id'), '')", + "" + ] + } + } + ] + }, + "actions": { + "SCIM_Delete_Null_Test_User": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users/', outputs('Compose_SCIM_Null_User_Id'))}", + "method": "DELETE", + "headers": { + "Authorization": "@{concat('Bearer ', parameters('scimBearerToken'))}", + "Accept": "@{parameters('scimContentType')}" + } + } + } + }, + "else": { + "actions": {} + } } }, "else": { @@ -2178,33 +2301,23 @@ "actions": {} }, "expression": { - "and": [ + "or": [ { "equals": [ - "@coalesce(triggerBody()?['initializationData']?['isGroupSupported'], false)", - true + "@triggerBody()?['EnabledTests']", + "All" ] }, { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIMTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIM_Group_Create_Test" - ] - } + "equals": [ + "@triggerBody()?['EnabledTests']", + "SCIMTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "SCIM_Group_Create_Test" ] } ] @@ -2285,24 +2398,75 @@ "inputs": { "from": "@body('Filter_Update_Typed_Attributes')", "select": { - "op": "Replace", + "op": "replace", "path": "@coalesce(item()?['name'], '')", "value": "@if(and(greater(length(coalesce(triggerBody()?['scimTargetUserValues'], json('[]'))), 1), contains(triggerBody()?['scimTargetUserValues'][1], coalesce(item()?['name'], ''))), triggerBody()?['scimTargetUserValues'][1][coalesce(item()?['name'], '')], if(equals(toLower(coalesce(item()?['type'], 'string')), 'integer'), rand(1, 99999), if(equals(toLower(coalesce(item()?['type'], 'string')), 'datetime'), utcNow(), concat('upd-', substring(guid(),0,8)))))" } } }, - "Compose_Update_Patch_Body": { + "Select_Update_NonVerbose_KeyValues": { + "runAfter": { + "Select_Update_Patch_Ops": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Select_Update_Patch_Ops')", + "select": "@concat('\"', replace(item()?['path'], '\"', '\\\"'), '\":', if(or(equals(item()?['value'], null), equals(string(item()?['value']), '')), 'null', if(isInt(item()?['value']), string(item()?['value']), concat('\"', replace(string(item()?['value']), '\"', '\\\"'), '\"'))))" + } + }, + "Filter_Update_MultiValued_Ops": { + "runAfter": { + "Select_Update_Patch_Ops": [ + "Succeeded" + ] + }, + "type": "Query", + "inputs": { + "from": "@body('Select_Update_Patch_Ops')", + "where": "@contains(coalesce(item()?['path'], ''), '[')" + } + }, + "Filter_Update_Batch_Ops": { "runAfter": { "Select_Update_Patch_Ops": [ "Succeeded" ] }, + "type": "Query", + "inputs": { + "from": "@body('Select_Update_Patch_Ops')", + "where": "@not(contains(coalesce(item()?['path'], ''), '['))" + } + }, + "Select_Update_Batch_KeyValues": { + "runAfter": { + "Filter_Update_Batch_Ops": [ + "Succeeded" + ] + }, + "type": "Select", + "inputs": { + "from": "@body('Filter_Update_Batch_Ops')", + "select": "@concat('\"', replace(item()?['path'], '\"', '\\\"'), '\":', if(or(equals(item()?['value'], null), equals(string(item()?['value']), '')), 'null', if(isInt(item()?['value']), string(item()?['value']), concat('\"', replace(string(item()?['value']), '\"', '\\\"'), '\"'))))" + } + }, + "Compose_Update_Patch_Body": { + "runAfter": { + "Select_Update_Batch_KeyValues": [ + "Succeeded" + ], + "Filter_Update_MultiValued_Ops": [ + "Succeeded" + ] + }, "type": "Compose", "inputs": { "schemas": [ "urn:ietf:params:scim:api:messages:2.0:PatchOp" ], - "Operations": "@body('Select_Update_Patch_Ops')" + "Operations": "@if(coalesce(triggerBody()?['initializationData']?['isVerbosePatchSupported'], true), body('Select_Update_Patch_Ops'), union(body('Filter_Update_MultiValued_Ops'), if(greater(length(body('Filter_Update_Batch_Ops')), 0), json(concat('[{\"op\":\"replace\",\"value\":{', join(body('Select_Update_Batch_KeyValues'), ','), '}}]')), json('[]'))))" } }, "Check_Update_Has_Eligible_Attrs": { @@ -2684,7 +2848,7 @@ "inputs": { "from": "@body('Filter_GroupUpdate_Eligible')", "select": { - "op": "Replace", + "op": "replace", "path": "@coalesce(item()?['name'], '')", "value": "@concat('upd-', substring(guid(),0,8))" } @@ -2798,33 +2962,23 @@ "actions": {} }, "expression": { - "and": [ + "or": [ + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, { "equals": [ - "@coalesce(triggerBody()?['initializationData']?['isGroupSupported'], false)", - true + "@triggerBody()?['EnabledTests']", + "SCIMTests" ] }, { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIMTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIM_Group_Update_Test" - ] - } + "equals": [ + "@triggerBody()?['EnabledTests']", + "SCIM_Group_Update_Test" ] } ] @@ -2845,7 +2999,7 @@ "body": "@{concat('grant_type=client_credentials&client_id=', encodeUriComponent(parameters('scimClientId')), '&client_secret=', encodeUriComponent(parameters('scimClientSecret')), if(empty(coalesce(parameters('scimOAuthScope'), '')), '', concat('&scope=', encodeUriComponent(parameters('scimOAuthScope')))))}" } }, - "Check_Token_Acquired": { + "Check_Token_Response": { "runAfter": { "Acquire_OAuth2_Token": [ "Succeeded", @@ -2879,85 +3033,127 @@ ] }, "actions": { - "SCIM_Validate_With_OAuth_Token": { - "type": "Http", - "inputs": { - "uri": "@{concat(parameters('scimEndpoint'), '/Users?startIndex=1&count=1')}", - "method": "GET", - "headers": { - "Authorization": "@{concat('Bearer ', body('Acquire_OAuth2_Token')?['access_token'])}", - "Accept": "@{parameters('scimContentType')}" - } - } + "Compose_Token_Validity_Minutes": { + "type": "Compose", + "inputs": "@div(coalesce(body('Acquire_OAuth2_Token')?['expires_in'], 3600), 60)" }, - "Compose_OAuth_Validation_Summary": { + "Check_Token_Validity_Range": { "runAfter": { - "SCIM_Validate_With_OAuth_Token": [ - "Succeeded", - "Failed", - "TimedOut" + "Compose_Token_Validity_Minutes": [ + "Succeeded" ] }, - "type": "Compose", - "inputs": { - "testName": "Validate_Credentials_Test", - "timestamp": "@utcNow()", - "authMode": "OAuth2ClientCredentials", - "tokenEndpoint": "@parameters('scimTokenEndpoint')", - "tokenAcquisition": { - "statusCode": "@outputs('Acquire_OAuth2_Token')['statusCode']", - "hasAccessToken": true, - "passed": true - }, - "scimEndpointValidation": { - "endpoint": "@concat(parameters('scimEndpoint'), '/Users?startIndex=1&count=1')", - "statusCode": "@coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0)", - "passed": "@and(greaterOrEquals(coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0), 200), less(coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0), 300))" - }, - "overallResult": "@if(and(greaterOrEquals(coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0), 200), less(coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0), 300)), 'PASSED', 'FAILED')" - } - } - }, - "else": { - "actions": { - "Compose_OAuth_Validation_Summary_TokenFail": { - "type": "Compose", - "inputs": { - "testName": "Validate_Credentials_Test", - "timestamp": "@utcNow()", - "authMode": "OAuth2ClientCredentials", - "tokenEndpoint": "@parameters('scimTokenEndpoint')", - "tokenAcquisition": { - "statusCode": "@coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_OAuth2_Token')?['access_token'], ''), ''))", - "passed": false + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@outputs('Compose_Token_Validity_Minutes')", + 60 + ] }, - "scimEndpointValidation": null, - "overallResult": "FAILED" - } - } - } - } - }, - "Set_ValidateCredentialsTestOutputs": { - "runAfter": { - "Check_Token_Acquired": [ - "Succeeded", - "Failed", - "TimedOut", - "Skipped" - ] - }, + { + "lessOrEquals": [ + "@outputs('Compose_Token_Validity_Minutes')", + 360 + ] + } + ] + }, + "actions": { + "SCIM_Validate_Get_Users": { + "type": "Http", + "inputs": { + "uri": "@{concat(parameters('scimEndpoint'), '/Users?startIndex=1&count=1')}", + "method": "GET", + "headers": { + "Authorization": "@{concat('Bearer ', body('Acquire_OAuth2_Token')?['access_token'])}", + "Accept": "@{parameters('scimContentType')}" + } + } + }, + "Compose_SCIM_Validation_Result": { + "runAfter": { + "SCIM_Validate_Get_Users": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "Compose", + "inputs": { + "passed": "@and(greaterOrEquals(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0), 200), less(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0), 300))", + "statusCode": "@coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0)", + "endpoint": "@concat(parameters('scimEndpoint'), '/Users?startIndex=1&count=1')", + "overallResult": "@if(and(greaterOrEquals(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0), 200), less(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0), 300)), 'PASSED', 'FAILED')" + } + } + }, + "else": { + "actions": { + "Compose_Token_Validity_Failure": { + "type": "Compose", + "inputs": { + "passed": false, + "validityMinutes": "@outputs('Compose_Token_Validity_Minutes')", + "expiresInSeconds": "@coalesce(body('Acquire_OAuth2_Token')?['expires_in'], 3600)", + "reason": "@if(less(outputs('Compose_Token_Validity_Minutes'), 60), 'Token validity is less than 60 minutes. Minimum required: 60 minutes.', 'Token validity exceeds 360 minutes (6 hours). Maximum allowed: 360 minutes.')", + "overallResult": "FAILED" + } + } + } + } + } + }, + "else": { + "actions": { + "Compose_Token_Acquisition_Failure": { + "type": "Compose", + "inputs": { + "passed": false, + "statusCode": "@coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_OAuth2_Token')?['access_token'], ''), ''))", + "tokenEndpoint": "@parameters('scimTokenEndpoint')", + "reason": "@if(or(less(coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0), 200), greaterOrEquals(coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0), 300)), concat('Token endpoint returned HTTP ', string(coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0)), '. Expected 2xx.'), 'Token endpoint returned 2xx but response did not contain an access_token field.')", + "overallResult": "FAILED" + } + } + } + } + }, + "Set_ValidateCredentialsTestOutputs": { + "runAfter": { + "Check_Token_Response": [ + "Succeeded", + "Failed", + "TimedOut", + "Skipped" + ] + }, "type": "SetVariable", "inputs": { "name": "ValidateCredentialsTestOutputs", "value": { - "overallResult": "@coalesce(outputs('Compose_OAuth_Validation_Summary')?['overallResult'], outputs('Compose_OAuth_Validation_Summary_TokenFail')?['overallResult'], 'FAILED')", - "result": "@if(equals(coalesce(outputs('Compose_OAuth_Validation_Summary')?['overallResult'], outputs('Compose_OAuth_Validation_Summary_TokenFail')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(empty(coalesce(outputs('Compose_OAuth_Validation_Summary'), json('null'))), 'FAILED - Action: Acquire_OAuth2_Token - token acquisition failed', 'FAILED - Action: SCIM_Validate_With_OAuth_Token - SCIM endpoint validation failed'))", - "summary": "@coalesce(outputs('Compose_OAuth_Validation_Summary'), outputs('Compose_OAuth_Validation_Summary_TokenFail'))", - "detailedResults": [], + "overallResult": "@coalesce(outputs('Compose_SCIM_Validation_Result')?['overallResult'], outputs('Compose_Token_Validity_Failure')?['overallResult'], outputs('Compose_Token_Acquisition_Failure')?['overallResult'], 'FAILED')", + "result": "@if(equals(coalesce(outputs('Compose_SCIM_Validation_Result')?['overallResult'], outputs('Compose_Token_Validity_Failure')?['overallResult'], outputs('Compose_Token_Acquisition_Failure')?['overallResult'], 'FAILED'), 'PASSED'), 'success', concat('FAILED - Action: ', coalesce(if(equals(coalesce(outputs('Compose_SCIM_Validation_Result')?['passed'], true), false), 'SCIM_Validate_Get_Users', null), if(not(empty(coalesce(outputs('Compose_Token_Validity_Failure'), json('null')))), 'Check_Token_Validity_Range', null), 'Acquire_OAuth2_Token'), ' - ', coalesce(if(equals(coalesce(outputs('Compose_SCIM_Validation_Result')?['passed'], true), false), concat('SCIM /Users returned HTTP ', string(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0)), '. Expected 2xx.'), null), outputs('Compose_Token_Validity_Failure')?['reason'], outputs('Compose_Token_Acquisition_Failure')?['reason'], 'Unexpected failure')))", + "summary": "@coalesce(outputs('Compose_SCIM_Validation_Result'), outputs('Compose_Token_Validity_Failure'), outputs('Compose_Token_Acquisition_Failure'))", + "detailedResults": { + "gate1_tokenAcquisition": { + "passed": "@not(equals(coalesce(outputs('Compose_Token_Validity_Minutes'), outputs('Compose_Token_Acquisition_Failure'), null), null))", + "statusCode": "@coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0)" + }, + "gate2_tokenLifetime": { + "passed": "@not(empty(coalesce(outputs('Compose_SCIM_Validation_Result'), outputs('Compose_Token_Validity_Failure'))))", + "validityMinutes": "@coalesce(outputs('Compose_Token_Validity_Minutes'), null)" + }, + "gate3_scimAccess": { + "passed": "@coalesce(outputs('Compose_SCIM_Validation_Result')?['passed'], false)", + "statusCode": "@coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], null)" + } + }, "errorDetails": { - "summary": "@if(equals(coalesce(outputs('Compose_OAuth_Validation_Summary')?['overallResult'], outputs('Compose_OAuth_Validation_Summary_TokenFail')?['overallResult'], 'FAILED'), 'PASSED'), null, if(empty(coalesce(outputs('Compose_OAuth_Validation_Summary'), json('null'))), concat('Token acquisition failed. Status: ', string(coalesce(outputs('Acquire_OAuth2_Token')?['statusCode'], 0))), concat('SCIM endpoint returned status: ', string(coalesce(outputs('SCIM_Validate_With_OAuth_Token')?['statusCode'], 0)))))" + "failedAction": "@coalesce(if(equals(coalesce(outputs('Compose_SCIM_Validation_Result')?['passed'], true), false), 'SCIM_Validate_Get_Users', null), if(not(empty(coalesce(outputs('Compose_Token_Validity_Failure'), json('null')))), 'Check_Token_Validity_Range', null), if(not(empty(coalesce(outputs('Compose_Token_Acquisition_Failure'), json('null')))), 'Acquire_OAuth2_Token', null), null)", + "reason": "@coalesce(if(equals(coalesce(outputs('Compose_SCIM_Validation_Result')?['passed'], true), false), concat('SCIM /Users returned HTTP ', string(coalesce(outputs('SCIM_Validate_Get_Users')?['statusCode'], 0)), '. Expected 2xx.'), null), outputs('Compose_Token_Validity_Failure')?['reason'], outputs('Compose_Token_Acquisition_Failure')?['reason'], null)" } } } @@ -2990,529 +3186,519 @@ }, "type": "If" }, - "Federated_Identity_Test": { + "Federated_Identity_Test": { "runAfter": {}, "actions": { - "Acquire_Entra_Token": { - "type": "Http", - "inputs": { - "uri": "@{concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')}", - "method": "POST", - "headers": { - "Content-Type": "application/x-www-form-urlencoded" - }, - "body": "@{concat('grant_type=client_credentials&client_id=', encodeUriComponent(parameters('federatedApplicationId')), '&client_secret=', encodeUriComponent(parameters('federatedApplicationClientSecret')), '&scope=', encodeUriComponent(concat('api://', parameters('federatedApplicationId'), '/.default')))}" + "Acquire_Entra_Token": { + "type": "Http", + "inputs": { + "uri": "@{concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')}", + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "body": "@{concat('grant_type=client_credentials&client_id=', encodeUriComponent(parameters('federatedApplicationId')), '&client_secret=', encodeUriComponent(parameters('federatedApplicationClientSecret')), '&scope=', encodeUriComponent(concat('api://', parameters('federatedApplicationId'), '/.default')))}" + } + }, + "Check_Entra_Token_Acquired": { + "runAfter": { + "Acquire_Entra_Token": ["Succeeded", "Failed", "TimedOut"] + }, + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", + 200 + ] + }, + { + "less": [ + "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", + 300 + ] + }, + { + "not": { + "equals": [ + "@coalesce(body('Acquire_Entra_Token')?['access_token'], '')", + "" + ] + } } + ] }, - "Check_Entra_Token_Acquired": { - "runAfter": { - "Acquire_Entra_Token": [ + "actions": { + "Check_Is_Google_SA_Flow": { + "type": "If", + "expression": { + "and": [ + { + "contains": [ + "@parameters('federatedTokenEndpoint')", + "googleapis.com" + ] + }, + { + "contains": [ + "@parameters('federatedTokenEndpoint')", + "serviceAccounts" + ] + } + ] + }, + "actions": { + "Acquire_Google_STS_Token": { + "type": "Http", + "inputs": { + "uri": "https://sts.googleapis.com/v1/token", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", + "audience": "@{parameters('federatedAudience')}", + "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", + "subject_token": "@{body('Acquire_Entra_Token')?['access_token']}", + "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", + "scope": "https://www.googleapis.com/auth/cloud-platform" + } + } + }, + "Check_Google_STS_Token": { + "runAfter": { + "Acquire_Google_STS_Token": [ "Succeeded", "Failed", "TimedOut" - ] - }, - "type": "If", - "expression": { - "and": [ + ] + }, + "type": "If", + "expression": { + "and": [ { - "greaterOrEquals": [ - "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", - 200 - ] + "greaterOrEquals": [ + "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", + 200 + ] }, { - "less": [ - "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", - 300 - ] + "less": [ + "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", + 300 + ] }, { - "not": { - "equals": [ - "@coalesce(body('Acquire_Entra_Token')?['access_token'], '')", - "" - ] - } + "not": { + "equals": [ + "@coalesce(body('Acquire_Google_STS_Token')?['access_token'], '')", + "" + ] + } } - ] - }, - "actions": { - "Check_Is_Google_SA_Flow": { + ] + }, + "actions": { + "Acquire_Google_SA_Token": { + "type": "Http", + "inputs": { + "uri": "@{parameters('federatedTokenEndpoint')}", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "@{concat('Bearer ', body('Acquire_Google_STS_Token')?['access_token'])}" + }, + "body": { + "scope": [ + "https://www.googleapis.com/auth/cloud-platform" + ] + } + } + }, + "Check_Google_SA_Token": { + "runAfter": { + "Acquire_Google_SA_Token": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, "type": "If", "expression": { - "and": [ - { - "contains": [ - "@parameters('federatedTokenEndpoint')", - "googleapis.com" - ] - }, - { - "contains": [ - "@parameters('federatedTokenEndpoint')", - "serviceAccounts" - ] - } - ] + "and": [ + { + "greaterOrEquals": [ + "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", + 200 + ] + }, + { + "less": [ + "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", + 300 + ] + }, + { + "not": { + "equals": [ + "@coalesce(body('Acquire_Google_SA_Token')?['accessToken'], '')", + "" + ] + } + } + ] }, "actions": { - "Acquire_Google_STS_Token": { - "type": "Http", - "inputs": { - "uri": "https://sts.googleapis.com/v1/token", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", - "audience": "@{parameters('federatedAudience')}", - "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", - "subject_token": "@{body('Acquire_Entra_Token')?['access_token']}", - "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", - "scope": "https://www.googleapis.com/auth/cloud-platform" - } - } - }, - "Check_Google_STS_Token": { - "runAfter": { - "Acquire_Google_STS_Token": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "If", - "expression": { - "and": [ - { - "greaterOrEquals": [ - "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", - 200 - ] - }, - { - "less": [ - "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", - 300 - ] - }, - { - "not": { - "equals": [ - "@coalesce(body('Acquire_Google_STS_Token')?['access_token'], '')", - "" - ] - } - } - ] - }, - "actions": { - "Acquire_Google_SA_Token": { - "type": "Http", - "inputs": { - "uri": "@{parameters('federatedTokenEndpoint')}", - "method": "POST", - "headers": { - "Content-Type": "application/json", - "Authorization": "@{concat('Bearer ', body('Acquire_Google_STS_Token')?['access_token'])}" - }, - "body": { - "scope": [ - "https://www.googleapis.com/auth/cloud-platform" - ] - } - } - }, - "Check_Google_SA_Token": { - "runAfter": { - "Acquire_Google_SA_Token": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "If", - "expression": { - "and": [ - { - "greaterOrEquals": [ - "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", - 200 - ] - }, - { - "less": [ - "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", - 300 - ] - }, - { - "not": { - "equals": [ - "@coalesce(body('Acquire_Google_SA_Token')?['accessToken'], '')", - "" - ] - } - } - ] - }, - "actions": { - "Compose_Federated_Validation_Summary_GoogleSA": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity_Google_SA", - "entraTokenAcquisition": { - "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", - "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", - "passed": true - }, - "googleStsTokenExchange": { - "endpoint": "https://sts.googleapis.com/v1/token", - "statusCode": "@outputs('Acquire_Google_STS_Token')['statusCode']", - "passed": true - }, - "googleSaImpersonation": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@outputs('Acquire_Google_SA_Token')['statusCode']", - "passed": true - }, - "overallResult": "PASSED" - } - } - }, - "else": { - "actions": { - "Compose_Federated_Validation_Summary_GoogleSA_Fail": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity_Google_SA", - "entraTokenAcquisition": { - "passed": true - }, - "googleStsTokenExchange": { - "passed": true - }, - "googleSaImpersonation": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_SA_Token')?['accessToken'], ''), ''))", - "passed": false - }, - "overallResult": "FAILED" - } - } - } - } - } - }, - "else": { - "actions": { - "Compose_Federated_Validation_Summary_GoogleSTS_Fail": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity_Google_SA", - "entraTokenAcquisition": { - "passed": true - }, - "googleStsTokenExchange": { - "endpoint": "https://sts.googleapis.com/v1/token", - "statusCode": "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_STS_Token')?['access_token'], ''), ''))", - "passed": false - }, - "overallResult": "FAILED" - } - } - } - } + "Compose_Federated_Validation_Summary_GoogleSA": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity_Google_SA", + "entraTokenAcquisition": { + "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", + "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", + "passed": true + }, + "googleStsTokenExchange": { + "endpoint": "https://sts.googleapis.com/v1/token", + "statusCode": "@outputs('Acquire_Google_STS_Token')['statusCode']", + "passed": true + }, + "googleSaImpersonation": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@outputs('Acquire_Google_SA_Token')['statusCode']", + "passed": true + }, + "overallResult": "PASSED" } + } }, "else": { - "actions": { - "Check_Is_Google_STS_Flow": { - "type": "If", - "expression": { - "and": [ - { - "contains": [ - "@parameters('federatedTokenEndpoint')", - "googleapis.com" - ] - } - ] - }, - "actions": { - "Acquire_Google_STS_Token_Only": { - "type": "Http", - "inputs": { - "uri": "@{parameters('federatedTokenEndpoint')}", - "method": "POST", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", - "audience": "@{parameters('federatedAudience')}", - "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", - "subject_token": "@{body('Acquire_Entra_Token')?['access_token']}", - "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", - "scope": "https://www.googleapis.com/auth/cloud-platform" - } - } - }, - "Check_Google_STS_Only_Token": { - "runAfter": { - "Acquire_Google_STS_Token_Only": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "If", - "expression": { - "and": [ - { - "greaterOrEquals": [ - "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", - 200 - ] - }, - { - "less": [ - "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", - 300 - ] - }, - { - "not": { - "equals": [ - "@coalesce(body('Acquire_Google_STS_Token_Only')?['access_token'], '')", - "" - ] - } - } - ] - }, - "actions": { - "Compose_Federated_Validation_Summary_GoogleSTSOnly": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity_Google_STS", - "entraTokenAcquisition": { - "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", - "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", - "passed": true - }, - "googleStsTokenExchange": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@outputs('Acquire_Google_STS_Token_Only')['statusCode']", - "passed": true - }, - "overallResult": "PASSED" - } - } - }, - "else": { - "actions": { - "Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity_Google_STS", - "entraTokenAcquisition": { - "passed": true - }, - "googleStsTokenExchange": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_STS_Token_Only')?['access_token'], ''), ''))", - "passed": false - }, - "overallResult": "FAILED" - } - } - } - } - } - }, - "else": { - "actions": { - "Acquire_Federated_Token": { - "type": "Http", - "inputs": { - "uri": "@{parameters('federatedTokenEndpoint')}", - "method": "POST", - "headers": { - "Content-Type": "application/x-www-form-urlencoded" - }, - "body": "@{concat('grant_type=client_credentials&client_id=', encodeUriComponent(parameters('federatedClientId')), '&client_assertion=', encodeUriComponent(body('Acquire_Entra_Token')?['access_token']), '&client_assertion_type=', encodeUriComponent('urn:ietf:params:oauth:client-assertion-type:jwt-bearer'), '&baseAddress=', encodeUriComponent(parameters('federatedBaseAddress')))}" - } - }, - "Check_Federated_Token_Acquired": { - "runAfter": { - "Acquire_Federated_Token": [ - "Succeeded", - "Failed", - "TimedOut" - ] - }, - "type": "If", - "expression": { - "and": [ - { - "greaterOrEquals": [ - "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", - 200 - ] - }, - { - "less": [ - "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", - 300 - ] - }, - { - "not": { - "equals": [ - "@coalesce(body('Acquire_Federated_Token')?['access_token'], '')", - "" - ] - } - } - ] - }, - "actions": { - "Compose_Federated_Validation_Summary": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity", - "entraTokenAcquisition": { - "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", - "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", - "passed": true - }, - "federatedTokenExchange": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@outputs('Acquire_Federated_Token')['statusCode']", - "passed": true - }, - "overallResult": "PASSED" - } - } - }, - "else": { - "actions": { - "Compose_Federated_Validation_Summary_TokenFail": { - "type": "Compose", - "inputs": { - "testName": "Federated_Identity_Test", - "timestamp": "@utcNow()", - "authMode": "FederatedIdentity", - "entraTokenAcquisition": { - "passed": true - }, - "federatedTokenExchange": { - "endpoint": "@parameters('federatedTokenEndpoint')", - "statusCode": "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_Federated_Token')?['access_token'], ''), ''))", - "passed": false - }, - "overallResult": "FAILED" - } - } - } - } - } - } - } - } + "actions": { + "Compose_Federated_Validation_Summary_GoogleSA_Fail": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity_Google_SA", + "entraTokenAcquisition": { + "passed": true + }, + "googleStsTokenExchange": { + "passed": true + }, + "googleSaImpersonation": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_SA_Token')?['accessToken'], ''), ''))", + "passed": false + }, + "overallResult": "FAILED" + } } + } } + } + }, + "else": { + "actions": { + "Compose_Federated_Validation_Summary_GoogleSTS_Fail": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity_Google_SA", + "entraTokenAcquisition": { + "passed": true + }, + "googleStsTokenExchange": { + "endpoint": "https://sts.googleapis.com/v1/token", + "statusCode": "@coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_STS_Token')?['access_token'], ''), ''))", + "passed": false + }, + "overallResult": "FAILED" + } + } + } } + } }, "else": { - "actions": { - "Compose_Federated_Validation_Summary_EntraFail": { - "type": "Compose", - "inputs": { + "actions": { + "Check_Is_Google_STS_Flow": { + "type": "If", + "expression": { + "and": [ + { + "contains": [ + "@parameters('federatedTokenEndpoint')", + "googleapis.com" + ] + } + ] + }, + "actions": { + "Acquire_Google_STS_Token_Only": { + "type": "Http", + "inputs": { + "uri": "@{parameters('federatedTokenEndpoint')}", + "method": "POST", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange", + "audience": "@{parameters('federatedAudience')}", + "requested_token_type": "urn:ietf:params:oauth:token-type:access_token", + "subject_token": "@{body('Acquire_Entra_Token')?['access_token']}", + "subject_token_type": "urn:ietf:params:oauth:token-type:id_token", + "scope": "https://www.googleapis.com/auth/cloud-platform" + } + } + }, + "Check_Google_STS_Only_Token": { + "runAfter": { + "Acquire_Google_STS_Token_Only": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", + 200 + ] + }, + { + "less": [ + "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", + 300 + ] + }, + { + "not": { + "equals": [ + "@coalesce(body('Acquire_Google_STS_Token_Only')?['access_token'], '')", + "" + ] + } + } + ] + }, + "actions": { + "Compose_Federated_Validation_Summary_GoogleSTSOnly": { + "type": "Compose", + "inputs": { "testName": "Federated_Identity_Test", "timestamp": "@utcNow()", - "authMode": "FederatedIdentity", + "authMode": "FederatedIdentity_Google_STS", "entraTokenAcquisition": { - "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", - "statusCode": "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", - "hasAccessToken": "@not(equals(coalesce(body('Acquire_Entra_Token')?['access_token'], ''), ''))", + "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", + "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", + "passed": true + }, + "googleStsTokenExchange": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@outputs('Acquire_Google_STS_Token_Only')['statusCode']", + "passed": true + }, + "overallResult": "PASSED" + } + } + }, + "else": { + "actions": { + "Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity_Google_STS", + "entraTokenAcquisition": { + "passed": true + }, + "googleStsTokenExchange": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_Google_STS_Token_Only')?['access_token'], ''), ''))", "passed": false + }, + "overallResult": "FAILED" + } + } + } + } + } + }, + "else": { + "actions": { + "Acquire_Federated_Token": { + "type": "Http", + "inputs": { + "uri": "@{parameters('federatedTokenEndpoint')}", + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded" + }, + "body": "@{concat('grant_type=client_credentials&client_id=', encodeUriComponent(parameters('federatedClientId')), '&client_assertion=', encodeUriComponent(body('Acquire_Entra_Token')?['access_token']), '&client_assertion_type=', encodeUriComponent('urn:ietf:params:oauth:client-assertion-type:jwt-bearer'), '&baseAddress=', encodeUriComponent(parameters('federatedBaseAddress')))}" + } + }, + "Check_Federated_Token_Acquired": { + "runAfter": { + "Acquire_Federated_Token": [ + "Succeeded", + "Failed", + "TimedOut" + ] + }, + "type": "If", + "expression": { + "and": [ + { + "greaterOrEquals": [ + "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", + 200 + ] }, - "federatedTokenExchange": null, - "scimEndpointValidation": null, - "overallResult": "FAILED" + { + "less": [ + "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", + 300 + ] + }, + { + "not": { + "equals": [ + "@coalesce(body('Acquire_Federated_Token')?['access_token'], '')", + "" + ] + } + } + ] + }, + "actions": { + "Compose_Federated_Validation_Summary": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity", + "entraTokenAcquisition": { + "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", + "statusCode": "@outputs('Acquire_Entra_Token')['statusCode']", + "passed": true + }, + "federatedTokenExchange": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@outputs('Acquire_Federated_Token')['statusCode']", + "passed": true + }, + "overallResult": "PASSED" + } + } + }, + "else": { + "actions": { + "Compose_Federated_Validation_Summary_TokenFail": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity", + "entraTokenAcquisition": { + "passed": true + }, + "federatedTokenExchange": { + "endpoint": "@parameters('federatedTokenEndpoint')", + "statusCode": "@coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_Federated_Token')?['access_token'], ''), ''))", + "passed": false + }, + "overallResult": "FAILED" + } + } + } } + } } + } } + } } + } }, - "Set_FederatedIdentityTestOutputs": { - "runAfter": { - "Check_Entra_Token_Acquired": [ - "Succeeded", - "Failed", - "TimedOut", - "Skipped" - ] - }, - "type": "SetVariable", - "inputs": { - "name": "FederatedIdentityTestOutputs", - "value": { - "overallResult": "@coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED')", - "result": "@if(equals(coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_EntraFail'), json('null')))), 'Entra token acquisition failed', 'Federated token exchange failed'))", - "summary": "@coalesce(outputs('Compose_Federated_Validation_Summary'), outputs('Compose_Federated_Validation_Summary_TokenFail'), outputs('Compose_Federated_Validation_Summary_GoogleSA'), outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail'), outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail'), outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly'), outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail'), outputs('Compose_Federated_Validation_Summary_EntraFail'))", - "detailedResults": [], - "errorDetails": { - "summary": "@if(equals(coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED'), 'PASSED'), null, if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_EntraFail'), json('null')))), concat('Entra token acquisition failed. Status: ', string(coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail'), json('null')))), concat('Google STS token exchange failed. Status: ', string(coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail'), json('null')))), concat('Google SA impersonation failed. Status: ', string(coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail'), json('null')))), concat('Google STS token exchange failed. Status: ', string(coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0))), concat('Federated token exchange failed. Status: ', string(coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0))))))))" - } - } + "else": { + "actions": { + "Compose_Federated_Validation_Summary_EntraFail": { + "type": "Compose", + "inputs": { + "testName": "Federated_Identity_Test", + "timestamp": "@utcNow()", + "authMode": "FederatedIdentity", + "entraTokenAcquisition": { + "endpoint": "@concat('https://login.microsoftonline.com/', parameters('federatedEntraTenantId'), '/oauth2/v2.0/token')", + "statusCode": "@coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0)", + "hasAccessToken": "@not(equals(coalesce(body('Acquire_Entra_Token')?['access_token'], ''), ''))", + "passed": false + }, + "federatedTokenExchange": null, + "scimEndpointValidation": null, + "overallResult": "FAILED" + } } + } } + }, + "Set_FederatedIdentityTestOutputs": { + "runAfter": { + "Check_Entra_Token_Acquired": [ + "Succeeded", + "Failed", + "TimedOut", + "Skipped" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "FederatedIdentityTestOutputs", + "value": { + "overallResult": "@coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED')", + "result": "@if(equals(coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED'), 'PASSED'), 'success', if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_EntraFail'), json('null')))), 'Entra token acquisition failed', 'Federated token exchange failed'))", + "summary": "@coalesce(outputs('Compose_Federated_Validation_Summary'), outputs('Compose_Federated_Validation_Summary_TokenFail'), outputs('Compose_Federated_Validation_Summary_GoogleSA'), outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail'), outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail'), outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly'), outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail'), outputs('Compose_Federated_Validation_Summary_EntraFail'))", + "detailedResults": [], + "errorDetails": { + "summary": "@if(equals(coalesce(outputs('Compose_Federated_Validation_Summary')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly')?['overallResult'], outputs('Compose_Federated_Validation_Summary_TokenFail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail')?['overallResult'], outputs('Compose_Federated_Validation_Summary_EntraFail')?['overallResult'], 'FAILED'), 'PASSED'), null, if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_EntraFail'), json('null')))), concat('Entra token acquisition failed. Status: ', string(coalesce(outputs('Acquire_Entra_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSTS_Fail'), json('null')))), concat('Google STS token exchange failed. Status: ', string(coalesce(outputs('Acquire_Google_STS_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSA_Fail'), json('null')))), concat('Google SA impersonation failed. Status: ', string(coalesce(outputs('Acquire_Google_SA_Token')?['statusCode'], 0))), if(not(empty(coalesce(outputs('Compose_Federated_Validation_Summary_GoogleSTSOnly_Fail'), json('null')))), concat('Google STS token exchange failed. Status: ', string(coalesce(outputs('Acquire_Google_STS_Token_Only')?['statusCode'], 0))), concat('Federated token exchange failed. Status: ', string(coalesce(outputs('Acquire_Federated_Token')?['statusCode'], 0))))))))" + } + } + } + } }, "else": { - "actions": {} + "actions": {} }, "expression": { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIMTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Federated_Identity_Test" - ] - } - ] + "or": [ + { + "equals": ["@triggerBody()?['EnabledTests']", "All"] + }, + { + "equals": ["@triggerBody()?['EnabledTests']", "SCIMTests"] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "Federated_Identity_Test" + ] + } + ] }, "type": "If" }, @@ -4087,33 +4273,23 @@ "actions": {} }, "expression": { - "and": [ + "or": [ { "equals": [ - "@coalesce(triggerBody()?['initializationData']?['isGroupSupported'], false)", - true + "@triggerBody()?['EnabledTests']", + "All" ] }, { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIMTests" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "SCIM_Group_Pagination_Test" - ] - } + "equals": [ + "@triggerBody()?['EnabledTests']", + "SCIMTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "SCIM_Group_Pagination_Test" ] } ] @@ -4130,12 +4306,7 @@ }, "Compose_Final_Results": { "runAfter": { - "SCIMTests_Scope": [ - "Succeeded", - "Failed", - "Skipped", - "TimedOut" - ] + "SCIMTests_Scope": ["Succeeded", "Failed", "Skipped", "TimedOut"] }, "type": "Compose", "inputs": { diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json index 89e5705..3ca3fa4 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/UserTests_Workflow.json @@ -231,7 +231,7 @@ "expression": "@greater(length(coalesce(body('SCIM_Query_Created_User')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -277,7 +277,7 @@ "expression": "@greater(length(coalesce(body('Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -558,7 +558,7 @@ "expression": "@greater(length(coalesce(body('Update_User_Verify_User_Created')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -602,7 +602,7 @@ "expression": "@greater(length(coalesce(body('Update_Verify_Create_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -711,7 +711,7 @@ "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -902,7 +902,7 @@ "expression": "@greater(length(coalesce(body('Update_Verify_Update_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -1031,7 +1031,7 @@ "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('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')))", + "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')))", @@ -1364,7 +1364,7 @@ "expression": "@greater(length(coalesce(body('DeleteUser_Check_User_Synced')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -1410,7 +1410,7 @@ "expression": "@greater(length(coalesce(body('DeleteUser_Verify_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -1631,7 +1631,7 @@ "expression": "@equals(coalesce(outputs('DeleteUser_Check_User_Deleted')?['statusCode'], 0), 404)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -1678,7 +1678,7 @@ "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": "PT40M" + "timeout": "PT15M" }, "type": "Until" }, @@ -2195,7 +2195,7 @@ "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -2277,7 +2277,7 @@ "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": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -2530,7 +2530,7 @@ "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -2576,7 +2576,7 @@ "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": "PT40M" + "timeout": "PT15M" }, "type": "Until" }, @@ -2804,7 +2804,7 @@ "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -2850,7 +2850,7 @@ "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": "PT40M" + "timeout": "PT15M" }, "type": "Until" }, @@ -3075,7 +3075,7 @@ "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -3121,7 +3121,7 @@ "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": "PT40M" + "timeout": "PT15M" }, "type": "Until" }, @@ -3457,7 +3457,7 @@ "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Step3_Fetch_User_By_Filter')?['Resources'], json('[]'))), 0)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -3503,7 +3503,7 @@ "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Create_Check_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -3652,7 +3652,7 @@ } } }, - "DisableUserTest_Disable_Step5_Disable_Or_Delete_User": { + "DisableUserTest_Disable_Step5_Disable_User": { "runAfter": { "DisableUserTest_Disable_Step4_Query_User_By_Id": [ "Succeeded", @@ -3662,11 +3662,11 @@ "type": "Http", "inputs": { "uri": "https://graph.microsoft.com/v1.0/users/@{body('DisableUserTest_Disable_Step2_Create_User')?['id']}", - "method": "@{if(triggerBody()?['IsSoftDeleted'], 'PATCH', 'DELETE')}", + "method": "PATCH", "headers": { "Content-Type": "application/json" }, - "body": "@{if(triggerBody()?['IsSoftDeleted'], json('{\"accountEnabled\": false}'), null)}", + "body": { "accountEnabled": false }, "authentication": { "type": "ManagedServiceIdentity", "audience": "https://graph.microsoft.com/" @@ -3703,14 +3703,14 @@ } }, "runAfter": { - "DisableUserTest_Disable_Step5_Disable_Or_Delete_User": [ + "DisableUserTest_Disable_Step5_Disable_User": [ "Succeeded" ] }, - "expression": "@if(triggerBody()?['IsSoftDeleted'], 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)), equals(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0))", + "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": "PT60M" + "timeout": "PT20M" }, "type": "Until" }, @@ -3719,7 +3719,7 @@ "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', if(triggerBody()?['IsSoftDeleted'], 'Disable', 'Delete'), '%27')}", + "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" @@ -3756,7 +3756,7 @@ "expression": "@greater(length(coalesce(body('DisableUserTest_Disable_Verify_Disable_Provisioning_Logs')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "timeout": "PT15M" }, "type": "Until" } @@ -3860,9 +3860,9 @@ "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(triggerBody()?['IsSoftDeleted'], 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'), if(equals(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), 'success', 'DisableUserTest: User still exists on SCIM server after hard delete')), concat('Failed Action: ', variables('DisableUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('DisableUserScopeExecutionStatus'), 'Succeeded'), if(triggerBody()?['IsSoftDeleted'], 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('SCIM verification failed: 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')))), if(equals(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0), null, concat('SCIM verification failed: User still exists on SCIM server after hard delete. Resources count: ', string(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))))))), 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')), if(triggerBody()?['IsSoftDeleted'], 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)), equals(length(coalesce(body('DisableUserTest_Disable_Step6_Check_User_After_Disable')?['Resources'], json('[]'))), 0))), 'PASSED', 'FAILED'), 'FAILED')" + "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": { @@ -3942,33 +3942,23 @@ "actions": {} }, "expression": { - "and": [ + "or": [ { "equals": [ - "@triggerBody()?['IsSoftDeleted']", - true + "@triggerBody()?['EnabledTests']", + "All" ] }, { - "or": [ - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "All" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "Disable_User_Test" - ] - }, - { - "equals": [ - "@triggerBody()?['EnabledTests']", - "UserTests" - ] - } + "equals": [ + "@triggerBody()?['EnabledTests']", + "Disable_User_Test" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" ] } ] @@ -3983,7 +3973,24 @@ "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", @@ -3996,12 +4003,31 @@ "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']}", - "givenName": "RestoreUser", - "surname": "Test", + "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": true, - "password": "@{concat('Tmp!', substring(guid(),0,8), '@', substring(guid(),24,8))}" + "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": { @@ -4056,7 +4082,7 @@ "expression": "@equals(coalesce(body('RestoreUserTest_SCIM_Get_Create')?['totalResults'], 0), 1)", "limit": { "count": 200, - "timeout": "PT60M" + "timeout": "PT20M" }, "actions": { "RestoreUserTest_SCIM_Get_Create": { @@ -4275,27 +4301,11 @@ "runAfter": {}, "type": "Scope", "actions": { - "RestoreUserTest_Get_AppRoleAssignment": { - "type": "Http", - "inputs": { - "method": "GET", - "uri": "@{concat('https://graph.microsoft.com/v1.0/users/', variables('restoreEntraUserId'), '/appRoleAssignments?$filter=resourceId%20eq%20', parameters('servicePrincipalId'))}", - "authentication": { - "type": "ManagedServiceIdentity", - "audience": "https://graph.microsoft.com/" - } - } - }, "RestoreUserTest_Unassign_AppRole": { - "runAfter": { - "RestoreUserTest_Get_AppRoleAssignment": [ - "Succeeded" - ] - }, "type": "Http", "inputs": { "method": "DELETE", - "uri": "@{concat('https://graph.microsoft.com/v1.0/servicePrincipals/', parameters('servicePrincipalId'), '/appRoleAssignments/', first(body('RestoreUserTest_Get_AppRoleAssignment')?['value'])?['id'])}", + "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/" @@ -4309,10 +4319,10 @@ ] }, "type": "Until", - "expression": "@or(equals(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 1), 0), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], true), false))", + "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": "PT60M" + "timeout": "PT20M" }, "actions": { "RestoreUserTest_SCIM_Get_Deprovision": { @@ -4355,14 +4365,14 @@ "expression": "@greater(length(coalesce(body('RestoreUserTest_Get_Deprovision_Log')?['value'], json('[]'))), 0)", "limit": { "count": 150, - "timeout": "PT40M" + "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%20or%20provisioningAction%20eq%20%27Delete%27)')}", + "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" }, @@ -4488,9 +4498,9 @@ "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(or(equals(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 1), 0), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], true), false)), 'success', 'RestoreUserTest: User still active on SCIM server after deassign'), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(or(equals(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 1), 0), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], true), false)), null, concat('SCIM verification failed: User not deprovisioned 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')), or(equals(coalesce(body('RestoreUserTest_SCIM_Get_Deprovision')?['totalResults'], 1), 0), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Deprovision')?['Resources'])?['active'], true), false))), 'PASSED', 'FAILED'), 'FAILED')" + "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": { @@ -4552,10 +4562,10 @@ ] }, "type": "Until", - "expression": "@if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Disable')), 'disable'), and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1))", + "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": "PT60M" + "timeout": "PT20M" }, "actions": { "RestoreUserTest_SCIM_Get_Restore": { @@ -4753,10 +4763,11 @@ "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": "@if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Disable')), 'disable'), 'SoftDeleteRestore', 'HardDeleteReprovision')", - "scimCheckResult": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Disable')), 'disable'), if(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), 'success', 'RestoreUserTest: User not re-active on SCIM server after reassign'), if(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), 'success', 'RestoreUserTest: User not re-created on SCIM server after reassign (hard delete re-provision)')), concat('Failed Action: ', variables('RestoreUserFailedActionName')))", - "scimCheckErrorDetails": "@if(equals(variables('RestoreUserScopeExecutionStatus'), 'Succeeded'), if(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Disable')), 'disable'), if(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), null, concat('SCIM verification failed: 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')))), if(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), null, concat('SCIM verification failed: User not re-created after hard delete. totalResults: ', string(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 'N/A'))))), concat('Scope execution failed. Failed action: ', variables('RestoreUserFailedActionName')))", - "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(equals(toLower(coalesce(first(body('RestoreUserTest_Get_Deprovision_Log')?['value'])?['provisioningAction'], 'Disable')), 'disable'), if(and(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), equals(coalesce(first(body('RestoreUserTest_SCIM_Get_Restore')?['Resources'])?['active'], false), true)), 'PASSED', 'FAILED'), if(greaterOrEquals(coalesce(body('RestoreUserTest_SCIM_Get_Restore')?['totalResults'], 0), 1), 'PASSED', 'FAILED')), 'FAILED'), 'FAILED')" + "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": { @@ -4775,6 +4786,7 @@ "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'))" @@ -4897,16 +4909,39 @@ "inputs": { "method": "POST", "uri": "https://graph.microsoft.com/v1.0/users", - "headers": { "Content-Type": "application/json" }, + "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": false, - "password": "@{concat('P@ss', substring(guid(), 0, 12), '!')}" + "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": { @@ -4915,18 +4950,50 @@ } } }, + "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_Create_Graph_User": ["Succeeded"] }, + "runAfter": { + "POD_SCIM_Pre_Check_User": [ + "Succeeded", + "Failed" + ] + }, "type": "Wait", - "inputs": { "interval": { "count": 30, "unit": "Second" } } + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } }, "POD_Assign_AppRole": { - "runAfter": { "POD_Delay_After_Create": ["Succeeded"] }, + "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" }, + "headers": { + "Content-Type": "application/json" + }, "body": { "principalId": "@{body('POD_Create_Graph_User')?['id']}", "resourceId": "@{parameters('servicePrincipalId')}", @@ -4939,17 +5006,32 @@ } }, "POD_Delay_After_AppRole": { - "runAfter": { "POD_Assign_AppRole": ["Succeeded"] }, + "runAfter": { + "POD_Assign_AppRole": [ + "Succeeded" + ] + }, "type": "Wait", - "inputs": { "interval": { "count": 60, "unit": "Second" } } + "inputs": { + "interval": { + "count": 60, + "unit": "Second" + } + } }, "POD_Get_Sync_Rules": { - "runAfter": { "POD_Delay_After_AppRole": ["Succeeded"] }, + "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" }, + "headers": { + "Accept": "application/json" + }, "authentication": { "type": "ManagedServiceIdentity", "audience": "https://graph.microsoft.com/" @@ -4957,12 +5039,18 @@ } }, "POD_Provision_On_Demand": { - "runAfter": { "POD_Get_Sync_Rules": ["Succeeded"] }, + "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" }, + "headers": { + "Content-Type": "application/json" + }, "body": { "parameters": [ { @@ -4983,12 +5071,26 @@ } }, "POD_Delay_After_Provision": { - "runAfter": { "POD_Provision_On_Demand": ["Succeeded", "Failed"] }, + "runAfter": { + "POD_Provision_On_Demand": [ + "Succeeded", + "Failed" + ] + }, "type": "Wait", - "inputs": { "interval": { "count": 2, "unit": "Minute" } } + "inputs": { + "interval": { + "count": 2, + "unit": "Minute" + } + } }, "POD_SCIM_Get_User": { - "runAfter": { "POD_Delay_After_Provision": ["Succeeded"] }, + "runAfter": { + "POD_Delay_After_Provision": [ + "Succeeded" + ] + }, "type": "Http", "inputs": { "method": "GET", @@ -5000,17 +5102,27 @@ } }, "POD_Until_Provisioning_Logs": { - "runAfter": { "POD_SCIM_Get_User": ["Succeeded", "Failed"] }, + "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" }, + "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" }, + "headers": { + "Accept": "application/json" + }, "authentication": { "type": "ManagedServiceIdentity", "audience": "https://graph.microsoft.com/" @@ -5018,9 +5130,19 @@ } }, "POD_Delay_Log_Poll": { - "runAfter": { "POD_Verify_Provisioning_Logs": ["Succeeded", "Failed"] }, + "runAfter": { + "POD_Verify_Provisioning_Logs": [ + "Succeeded", + "Failed" + ] + }, "type": "Wait", - "inputs": { "interval": { "count": 30, "unit": "Second" } } + "inputs": { + "interval": { + "count": 30, + "unit": "Second" + } + } } } } @@ -5028,7 +5150,11 @@ }, "POD_User_Analyze_Results": { "runAfter": { - "POD_User_Test_Actions": ["Succeeded", "Failed", "TimedOut"] + "POD_User_Test_Actions": [ + "Succeeded", + "Failed", + "TimedOut" + ] }, "type": "Compose", "inputs": { @@ -5048,7 +5174,11 @@ } }, "Set_PODUserTestOutputs": { - "runAfter": { "POD_User_Analyze_Results": ["Succeeded"] }, + "runAfter": { + "POD_User_Analyze_Results": [ + "Succeeded" + ] + }, "type": "SetVariable", "inputs": { "name": "PODUserTestOutputs", @@ -5063,7 +5193,13 @@ } }, "POD_User_Cleanup_SCIM_Delete": { - "runAfter": { "Set_PODUserTestOutputs": ["Succeeded", "Failed", "Skipped"] }, + "runAfter": { + "Set_PODUserTestOutputs": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, "type": "Http", "inputs": { "method": "DELETE", @@ -5075,7 +5211,13 @@ } }, "POD_User_Cleanup_Graph_Delete": { - "runAfter": { "POD_User_Cleanup_SCIM_Delete": ["Succeeded", "Failed", "Skipped"] }, + "runAfter": { + "POD_User_Cleanup_SCIM_Delete": [ + "Succeeded", + "Failed", + "Skipped" + ] + }, "type": "Http", "inputs": { "method": "DELETE", @@ -5090,9 +5232,24 @@ "runAfter": {}, "expression": { "or": [ - { "equals": ["@triggerBody()?['EnabledTests']", "All"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "UserTests"] }, - { "equals": ["@triggerBody()?['EnabledTests']", "POD_User_Test"] } + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "All" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "UserTests" + ] + }, + { + "equals": [ + "@triggerBody()?['EnabledTests']", + "POD_User_Test" + ] + } ] }, "type": "If" diff --git a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION index e8f1734..4fedf1d 100644 --- a/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION +++ b/Microsoft.SCIM.LogicAppValidationTemplate/StandardLogicApp/VERSION @@ -1 +1 @@ -6.1 \ No newline at end of file +7.0