# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 # # Cognito User Profiles Export Reference Architecture # # template for cognito-user-profiles-export-reference-architecture # **DO NOT DELETE** # # author: aws-solutions-builder@ AWSTemplateFormatVersion: 2010-09-09 Description: (SO0126s) - StackSet template for Cognito User Profiles Export Reference Architecture. Version VERSION_PLACEHOLDER Parameters: PrimaryRegion: Type: String AllowedPattern: ^[\w-]+$ CognitoTPS: Type: String Default: "10" AllowedValues: ["1", "5", "10"] ExportFrequency: Type: String Default: "EVERY_DAY" AllowedValues: ["EVERY_DAY", "EVERY_7_DAYS", "EVERY_30_DAYS"] NotificationEmail: Type: String AllowedPattern: "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" SendAnonymousData: Type: String ParentStackName: Type: String SnsPreference: Type: String Default: "INFO_AND_ERRORS" AllowedValues: ["INFO_AND_ERRORS", "ERRORS_ONLY"] Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Backup Process Configuration. Please do not change these values here. To update them, please update the solution's main CloudFormation stack Parameters: - NotificationEmail - SnsPreference - SendAnonymousData - ExportFrequency - CognitoTPS - Label: default: Fixed Parameters. Please do not change these values. Parameters: - ParentStackName - PrimaryRegion ParameterLabels: ExportFrequency: default: "ExportFrequency: The frequency at which the Export Workflow will run" CognitoTPS: default: "CognitoTPS: The amount of times a Cognito API will be called per second" NotificationEmail: default: "NotificationEmail: Email address for SNS notifications. Subscribed users will receive receive notifications if an issue is detected" SnsPreference: default: "SnsPreference: INFO_AND_ERRORS - The solution will publish a message to the SNS topic each time a workflow completes and if there are errors detected. ERRORS_ONLY - The solution will only publish messages if an error is detected" PrimaryRegion: default: "PrimaryRegion: The region in which you launched this solution's main CloudFormation stack" ParentStackName: default: "ParentStackName: The name given to this solution's main CloudFormation stack" SendAnonymousData: default: "SendAnonymousData: Flag indicating whether or not to send anonymous operational metrics. Value comes from the 'SendAnonymousData' mapping in the solution's main CloudFormation template" Mappings: Solution: Config: SolutionId: SO0126 Version: VERSION_PLACEHOLDER S3BucketPrefix: BUCKET_NAME_PLACEHOLDER S3KeyPrefix: SOLUTION_NAME_PLACEHOLDER/VERSION_PLACEHOLDER Conditions: IsPrimaryRegion: !Equals [ !Ref "AWS::Region", !Ref PrimaryRegion ] ExportDaily: !Equals [ !Ref ExportFrequency, "EVERY_DAY" ] ExportWeekly: !Equals [ !Ref ExportFrequency, "EVERY_7_DAYS" ] ExportMonthly: !And - !Not [ Condition: ExportDaily ] - !Not [ Condition: ExportWeekly ] Resources: ####################################### # These resources will be created in both the primary and backup regions ####################################### StackSetConstantsCustomResourceLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${AWS::StackName}-${AWS::Region} Description: Retrieves constants generated by the main stack for use in this StackSet Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "custom-resources.zip"]] Handler: "custom-resources/stackset-constants.handler" Role: !GetAtt StackSetConstantsCustomResourceLambdaRole.Arn Runtime: nodejs14.x Timeout: 60 MemorySize: 128 Environment: Variables: SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] StackSetConstantsCustomResourceLambdaRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${AWS::StackName}-${AWS::Region} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - Effect: Allow Action: - ssm:GetParameter Resource: - !Sub arn:${AWS::Partition}:ssm:${PrimaryRegion}:${AWS::AccountId}:parameter/${ParentStackName}-${PrimaryRegion}/fixed-solution-parameters StackSetConstantsCustomResource: Type: Custom::StackSetConstants Properties: ServiceToken: !GetAtt StackSetConstantsCustomResourceLambda.Arn ParentStackName: !Ref ParentStackName PrimaryRegion: !Ref PrimaryRegion ImportWorkflowServiceRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W76 reason: SPCM for IAM policy document is higher than 25 - All permissions are required. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - states.amazonaws.com Action: - sts:AssumeRole Path: /service-role/ Policies: - PolicyName: ImportWorkflowServiceRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: lambda:InvokeFunction Resource: - !GetAtt ImportCheckNewUserPoolLambda.Arn - !GetAtt ImportScanTableLambda.Arn - !GetAtt ImportNewUsersLambda.Arn - !GetAtt ImportCheckImportJobLambda.Arn - !GetAtt ImportUpdateNewUsersLambda.Arn - !GetAtt CheckExecutionsLambda.Arn - !GetAtt WorkflowMessageBrokerLambda.Arn - !GetAtt CheckWorkflowQueuesLambda.Arn ImportWorkflow: Type: AWS::StepFunctions::StateMachine Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} RoleArn: !GetAtt ImportWorkflowServiceRole.Arn DefinitionString: !Sub | { "Comment": "SOLUTION_NAME_PLACEHOLDER VERSION_PLACEHOLDER: Restores a new user pool with the state reflected in the backup table", "StartAt": "CheckExecutionsLambda", "States": { "CheckExecutionsLambda": { "Type": "Task", "Resource": "${CheckExecutionsLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Only this execution running for this state machine?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Only this execution running for this state machine?": { "Type": "Choice", "Choices": [ { "Variable": "$.OnlyThisStateMachineExecution", "BooleanEquals": true, "Next": "Yes - only this execution is running" } ], "Default": "No - at least one other execution is running" }, "Yes - only this execution is running": { "Type": "Pass", "Next": "Wait 1 minute" }, "No - at least one other execution is running": { "Type": "Fail", "Error": "AnotherExecutionRunning", "Cause": "At least one other execution for this state machine was found to be running. Please check logs for CheckExecutionsLambda for details" }, "Wait 1 minute": { "Comment": "Wait 1 minute to allow queues to achieve consistency", "Type": "Wait", "Seconds": 60, "Next": "CheckWorkflowQueuesLambda" }, "CheckWorkflowQueuesLambda": { "Type": "Task", "Resource": "${CheckWorkflowQueuesLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "ImportCheckNewUserPoolLambda", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "ImportCheckNewUserPoolLambda": { "Type": "Task", "Resource": "${ImportCheckNewUserPoolLambda.Arn}", "OutputPath": "$.result", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Is the new user pool empty?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Is the new user pool empty?": { "Type": "Choice", "Choices": [ { "Variable": "$.NewUserPoolEmpty", "BooleanEquals": true, "Next": "Yes - the new user pool is empty" } ], "Default": "No - the new user pool is not empty" }, "No - the new user pool is not empty": { "Type": "Fail", "Error": "NewUserPoolNotEmpty", "Cause": "Please check logs for ImportCheckNewUserPoolLambda for details" }, "Yes - the new user pool is empty": { "Type": "Pass", "Next": "Parallel: Scan table and import users" }, "Parallel: Scan table and import users":{ "Type": "Parallel", "Branches": [ { "StartAt": "ImportScanTableLambda", "States": { "ImportScanTableLambda": { "Type": "Task", "Resource": "${ImportScanTableLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Finished Scanning Table?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "Parallel: WorkflowErrorHandlerLambda (Scan Table)" } ] }, "Finished Scanning Table?": { "Type": "Choice", "Choices": [ { "Variable": "$.AllGroupsProcessed", "StringEquals": "Yes", "Next": "Yes" }, { "Variable": "$.AllGroupsProcessed", "StringEquals": "No", "Next": "No" } ], "Default": "Yes" }, "Yes": { "Type": "Pass", "End": true }, "No": { "Type": "Pass", "Next": "ImportScanTableLambda" }, "Parallel: WorkflowErrorHandlerLambda (Scan Table)": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Parallel: Unexpected workflow error (Scan Table)" }, "Parallel: Unexpected workflow error (Scan Table)": { "Type": "Fail", "Error": "UnexpectedErrorInWorkflow", "Cause": "An unexpected error occured while executing this workflow. Please check logs for the State Machine task that generated this error for details" } } }, { "StartAt": "Parallel: Wait 60 seconds", "States": { "Parallel: Wait 60 seconds": { "Comment": "Wait 60 seconds to allow messages to be queued", "Type": "Wait", "Seconds": 60, "Next": "Parallel: ImportNewUsers" }, "Parallel: ImportNewUsers": { "Type": "Task", "Resource": "${ImportNewUsersLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Parallel: Is User Import Job Running?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "Parallel: WorkflowErrorHandlerLambda" } ] }, "Parallel: Is User Import Job Running?": { "Type": "Choice", "Choices": [ { "Or": [ { "Variable": "$.ImportJobStatus", "StringEquals": "Pending" }, { "Variable": "$.ImportJobStatus", "StringEquals": "InProgress" } ], "Next": "Parallel: Yes - CSV is being imported" }, { "Or": [ { "Variable": "$.ImportJobStatus", "StringEquals": "Succeeded" }, { "Variable": "$.ImportJobStatus", "StringEquals": "" } ], "Next": "Parallel: No import job running" } ] }, "Parallel: Yes - CSV is being imported": { "Type": "Pass", "Next": "Parallel: Wait 30 seconds" }, "Parallel: Wait 30 seconds": { "Comment": "Wait 30 seconds before checking the import job again", "Type": "Wait", "Seconds": 30, "Next": "Parallel: CheckUserImportJob" }, "Parallel: CheckUserImportJob": { "Type": "Task", "Resource": "${ImportCheckImportJobLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Parallel: Is User Import Job Running?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "Parallel: WorkflowErrorHandlerLambda" } ] }, "Parallel: No import job running": { "Type": "Pass", "Next": "Parallel: Is New Users Queue Empty?" }, "Parallel: Is New Users Queue Empty?": { "Type": "Choice", "Choices": [ { "Variable": "$.QueueEmpty", "BooleanEquals": true, "Next": "Parallel: Yes - New Users Queue has been drained" }, { "Variable": "$.QueueEmpty", "BooleanEquals": false, "Next": "Parallel: No - New Users Queue is not empty" } ] }, "Parallel: No - New Users Queue is not empty": { "Type": "Pass", "Next": "Parallel: ImportNewUsers" }, "Parallel: Yes - New Users Queue has been drained": { "Type": "Pass", "End": true }, "Parallel: WorkflowErrorHandlerLambda": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Parallel: Unexpected workflow error" }, "Parallel: Unexpected workflow error": { "Type": "Fail", "Error": "UnexpectedErrorInWorkflow", "Cause": "An unexpected error occured while executing this workflow. Please check logs for the State Machine task that generated this error for details" } } } ], "Next": "Wait 60 seconds" }, "Wait 60 seconds": { "Comment": "Wait 60 seconds to allow users to be queued", "Type": "Wait", "Seconds": 60, "Next": "ImportNewUsers" }, "ImportNewUsers": { "Type": "Task", "Resource": "${ImportNewUsersLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Is User Import Job Running?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Is User Import Job Running?": { "Type": "Choice", "Choices": [ { "Or": [ { "Variable": "$.ImportJobStatus", "StringEquals": "Pending" }, { "Variable": "$.ImportJobStatus", "StringEquals": "InProgress" } ], "Next": "Yes - CSV is being imported" }, { "Or": [ { "Variable": "$.ImportJobStatus", "StringEquals": "Succeeded" }, { "Variable": "$.ImportJobStatus", "StringEquals": "" } ], "Next": "No import job running" } ] }, "Yes - CSV is being imported": { "Type": "Pass", "Next": "Wait 30 seconds" }, "Wait 30 seconds": { "Comment": "Wait 30 seconds before checking the import job again", "Type": "Wait", "Seconds": 30, "Next": "CheckUserImportJob" }, "CheckUserImportJob": { "Type": "Task", "Resource": "${ImportCheckImportJobLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Is User Import Job Running?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "No import job running": { "Type": "Pass", "Next": "Is New Users Queue Empty?" }, "Is New Users Queue Empty?": { "Type": "Choice", "Choices": [ { "Variable": "$.QueueEmpty", "BooleanEquals": true, "Next": "Yes - New Users Queue has been drained" }, { "Variable": "$.QueueEmpty", "BooleanEquals": false, "Next": "No - New Users Queue is not empty" } ] }, "No - New Users Queue is not empty": { "Type": "Pass", "Next": "ImportNewUsers" }, "Yes - New Users Queue has been drained": { "Type": "Pass", "Next": "UpdateNewUsers" }, "UpdateNewUsers": { "Type": "Task", "Resource": "${ImportUpdateNewUsersLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Is New Users Updates Queue Empty?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Is New Users Updates Queue Empty?": { "Type": "Choice", "Choices": [ { "Variable": "$.QueueEmpty", "BooleanEquals": true, "Next": "Yes - New Users Updates Queue has been drained" }, { "Variable": "$.QueueEmpty", "BooleanEquals": false, "Next": "No - New Users Updates Queue is not empty" } ] }, "No - New Users Updates Queue is not empty": { "Type": "Pass", "Next": "UpdateNewUsers" }, "Yes - New Users Updates Queue has been drained": { "Type": "Pass", "Next": "WorkflowCleanupLambda" }, "WorkflowCleanupLambda": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "End": true, "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "WorkflowErrorHandlerLambda": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Unexpected workflow error" }, "Unexpected workflow error": { "Type": "Fail", "Error": "UnexpectedErrorInWorkflow", "Cause": "An unexpected error occured while executing this workflow. Please check logs for the State Machine task that generated this error for details" } } } CheckWorkflowQueuesLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Validates that the Queue(s) used by the workflow are empty Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-common.zip"]] Handler: "workflow-common/check-workflow-queues.handler" Role: !GetAtt CheckWorkflowQueuesLambdaRole.Arn Runtime: nodejs14.x Timeout: 60 MemorySize: 128 Environment: Variables: NEW_USERS_QUEUE_URL: !Ref ImportNewUsersQueue UPDATES_QUEUE_URL: !Ref ImportNewUsersUpdatesQueue SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] CheckWorkflowQueuesLambdaRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: CheckWorkflowQueuesLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sqs:GetQueueAttributes Resource: - !GetAtt ImportNewUsersQueue.Arn - !GetAtt ImportNewUsersUpdatesQueue.Arn ImportCheckNewUserPoolLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Returns true only if the new user pool is empty Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-import.zip"]] Handler: "workflow-import/check-new-user-pool.handler" Role: !GetAtt ImportCheckNewUserPoolLambdaRole.Arn Runtime: nodejs14.x Timeout: 60 MemorySize: 128 Environment: Variables: SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ImportCheckNewUserPoolLambdaRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ImportCheckNewUserPoolLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - cognito-idp:ListUsers - cognito-idp:ListGroups Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* ImportScanTableLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Scans the backup table and sorts items into queues for later processing Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-import.zip"]] Handler: "workflow-import/scan-table.handler" Role: !GetAtt ImportScanTableLambdaRole.Arn Runtime: nodejs14.x Timeout: 300 MemorySize: 128 Environment: Variables: BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName TYPE_GROUP: !Join ["-", [ "GROUP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] TYPE_USER: !Join ["-", [ "USER", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] TYPE_TIMESTAMP: !Join ["-", [ "SYNC_TIMESTAMP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] NEW_USERS_QUEUE_URL: !Ref ImportNewUsersQueue NEW_USERS_UPDATES_QUEUE_URL: !Ref ImportNewUsersUpdatesQueue COGNITO_TPS: !Ref CognitoTPS SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ImportScanTableLambdaRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ImportScanTableLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - dynamodb:Scan Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} - Effect: Allow Action: - cognito-idp:CreateGroup Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* - Effect: Allow Action: - sqs:SendMessage - sqs:SendMessageBatch Resource: - !GetAtt ImportNewUsersQueue.Arn - !GetAtt ImportNewUsersUpdatesQueue.Arn ImportNewUsersQueue: Type: AWS::SQS::Queue Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} QueueName: !Sub "${StackSetConstantsCustomResource.ImportNewUsersQueueNamePrefix}-${StackSetConstantsCustomResource.SolutionInstanceUUID}" KmsMasterKeyId: alias/aws/sqs VisibilityTimeout: 30 RedrivePolicy: deadLetterTargetArn: !GetAtt ImportNewUsersDeadLetterQueue.Arn maxReceiveCount: 2 ImportNewUsersUpdatesQueue: Type: AWS::SQS::Queue Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} QueueName: !Sub "${StackSetConstantsCustomResource.ImportNewUsersQueueNamePrefix}-${StackSetConstantsCustomResource.SolutionInstanceUUID}-updates" KmsMasterKeyId: alias/aws/sqs VisibilityTimeout: 30 RedrivePolicy: deadLetterTargetArn: !GetAtt ImportNewUsersDeadLetterQueue.Arn maxReceiveCount: 2 ImportNewUsersDeadLetterQueue: Type: AWS::SQS::Queue Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} QueueName: !Sub "${StackSetConstantsCustomResource.ImportNewUsersQueueNamePrefix}-${StackSetConstantsCustomResource.SolutionInstanceUUID}-dlq" KmsMasterKeyId: alias/aws/sqs VisibilityTimeout: 30 ImportNewUsersLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Imports users to the new user pool Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-import.zip"]] Handler: "workflow-import/import-users.handler" Role: !GetAtt ImportNewUsersLambdaRole.Arn Runtime: nodejs14.x Timeout: 900 MemorySize: 1024 # Higher memory size to allow for large CSV files to be uploaded to the Cognito User Import Job Environment: Variables: BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName TYPE_USER: !Join ["-", [ "USER", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] TYPE_GROUP: !Join ["-", [ "GROUP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] COGNITO_TPS: !Ref CognitoTPS NEW_USERS_QUEUE_URL: !Ref ImportNewUsersQueue NEW_USERS_UPDATES_QUEUE_URL: !Ref ImportNewUsersUpdatesQueue USER_IMPORT_CLOUDWATCH_ROLE_ARN: !GetAtt CognitoUserImportCloudWatchLogsRole.Arn USER_IMPORT_JOB_MAPPING_FILES_BUCKET: !Ref UserImportJobMappingFiles SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ImportNewUsersLambdaRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W76 reason: SPCM for IAM policy document is higher than 25 - All permissions are required. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ImportNewUsersLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - dynamodb:Scan # - dynamodb:Query Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} - Effect: Allow Action: - cognito-idp:DescribeUserImportJob - cognito-idp:StartUserImportJob - cognito-idp:CreateUserImportJob - cognito-idp:GetCSVHeader # - cognito-idp:AdminDisableUser # - cognito-idp:AdminAddUserToGroup Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* - Effect: Allow Action: - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:GetQueueAttributes Resource: - !GetAtt ImportNewUsersQueue.Arn - !GetAtt ImportNewUsersUpdatesQueue.Arn - Effect: Allow Action: - iam:PassRole Resource: - !GetAtt CognitoUserImportCloudWatchLogsRole.Arn - Effect: Allow Action: - s3:PutObject Resource: - !Sub arn:${AWS::Partition}:s3:::${UserImportJobMappingFiles}/import-*-user-mapping.csv - Effect: Allow Action: - sns:Publish Resource: - !Ref NotificationTopic ImportCheckImportJobLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Checks the user import job Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-import.zip"]] Handler: "workflow-import/import-users.handler" Role: !GetAtt ImportCheckImportJobLambdaRole.Arn Runtime: nodejs14.x Timeout: 60 MemorySize: 128 Environment: Variables: SEND_METRIC: !Ref SendAnonymousData METRICS_ANONYMOUS_UUID: !GetAtt StackSetConstantsCustomResource.AnonymousDataUUID IS_PRIMARY_REGION: !If [ IsPrimaryRegion, "Yes", "No" ] BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName TYPE_USER: !Join ["-", [ "USER", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] TYPE_GROUP: !Join ["-", [ "GROUP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] COGNITO_TPS: !Ref CognitoTPS NEW_USERS_QUEUE_URL: !Ref ImportNewUsersQueue NEW_USERS_UPDATES_QUEUE_URL: !Ref ImportNewUsersUpdatesQueue USER_IMPORT_CLOUDWATCH_ROLE_ARN: !GetAtt CognitoUserImportCloudWatchLogsRole.Arn USER_IMPORT_JOB_MAPPING_FILES_BUCKET: !Ref UserImportJobMappingFiles SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ImportCheckImportJobLambdaRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W76 reason: SPCM for IAM policy document is higher than 25 - All permissions are required. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ImportCheckImportJobLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - cognito-idp:DescribeUserImportJob Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* - Effect: Allow Action: - sns:Publish Resource: - !Ref NotificationTopic ImportUpdateNewUsersLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Updates users that were recently imported to the new user pool Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-import.zip"]] Handler: "workflow-import/update-new-users.handler" Role: !GetAtt ImportUpdateNewUsersLambdaRole.Arn Runtime: nodejs14.x Timeout: 900 MemorySize: 128 Environment: Variables: BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName TYPE_USER: !Join ["-", [ "USER", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] TYPE_GROUP: !Join ["-", [ "GROUP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] COGNITO_TPS: !Ref CognitoTPS NEW_USERS_QUEUE_URL: !Ref ImportNewUsersQueue NEW_USERS_UPDATES_QUEUE_URL: !Ref ImportNewUsersUpdatesQueue USER_IMPORT_CLOUDWATCH_ROLE_ARN: !GetAtt CognitoUserImportCloudWatchLogsRole.Arn USER_IMPORT_JOB_MAPPING_FILES_BUCKET: !Ref UserImportJobMappingFiles SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ImportUpdateNewUsersLambdaRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W76 reason: SPCM for IAM policy document is higher than 25 - All permissions are required. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ImportUpdateNewUsersLambdaRolePolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - cognito-idp:AdminDisableUser - cognito-idp:AdminAddUserToGroup Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/* - Effect: Allow Action: - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:GetQueueAttributes Resource: - !GetAtt ImportNewUsersUpdatesQueue.Arn - Effect: Allow Action: - iam:PassRole Resource: - !GetAtt CognitoUserImportCloudWatchLogsRole.Arn - Effect: Allow Action: - sns:Publish Resource: - !Ref NotificationTopic NotificationTopic: Type: AWS::SNS::Topic Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} DisplayName: !Sub "Cognito User Profiles Export Reference Architecture: ${AWS::Region} (${StackSetConstantsCustomResource.FormattedStackName})" TopicName: !Sub "cognito-user-profiles-export-reference-architecture-${AWS::Region}-${StackSetConstantsCustomResource.SolutionInstanceUUID}" KmsMasterKeyId: alias/aws/sns Subscription: - Endpoint: !Ref NotificationEmail Protocol: email WorkflowMessageBrokerLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Handles unexpected errors thrown by lambda tasks in the solution's step functions workflows Handler: "workflow-common/message-broker.handler" Role: !GetAtt WorkflowMessageBrokerLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-common.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: SEND_METRIC: !Ref SendAnonymousData METRICS_ANONYMOUS_UUID: !GetAtt StackSetConstantsCustomResource.AnonymousDataUUID IS_PRIMARY_REGION: !If [ IsPrimaryRegion, "Yes", "No" ] NOTIFICATION_TOPIC: !Ref NotificationTopic SNS_MESSAGE_PREFERENCE: !Ref SnsPreference BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName TYPE_TIMESTAMP: !Join ["-", [ "SYNC_TIMESTAMP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] WorkflowMessageBrokerLambdaRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: workflow-message-broker-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - sns:Publish Resource: - !Ref NotificationTopic - Effect: Allow Action: - dynamodb:PutItem Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} CognitoUserImportCloudWatchLogsRole: Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - cognito-idp.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: cognito-user-import-cw-logs-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:DescribeLogStreams - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/cognito/* UserImportJobMappingFiles: Type: AWS::S3::Bucket DeletionPolicy: Retain UpdateReplacePolicy: Retain Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} BucketName: !Sub ${StackSetConstantsCustomResource.UserImportJobMappingFileBucketPrefix}-${AWS::Region} PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True LoggingConfiguration: DestinationBucketName: !Ref UserImportJobMappingFilesLogsBucket LogFilePrefix: user-import-job-mapping-files/ BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms Metadata: cfn_nag: rules_to_suppress: - id: W51 reason: Policy not required for this bucket. UserImportJobMappingFilesLogsBucket: Type: AWS::S3::Bucket Metadata: cfn_nag: rules_to_suppress: - id: W35 reason: Logging not enabled, as this is the logging destination for the other s3 buckets in this template. - id: W51 reason: Policy not required for this bucket. DeletionPolicy: Retain UpdateReplacePolicy: Retain Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} BucketName: !Sub ${StackSetConstantsCustomResource.UserImportJobMappingFileBucketPrefix}-${AWS::Region}-logs PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: aws:kms S3ServerAccessLogsPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref UserImportJobMappingFilesLogsBucket PolicyDocument: Version: '2012-10-17' Statement: - Sid: S3ServerAccessLogsPolicy Effect: Allow Principal: Service: logging.s3.amazonaws.com Action: - s3:PutObject Resource: Fn::Join: [ '', [ 'arn:', !Ref AWS::Partition, ':s3:::', !Ref UserImportJobMappingFilesLogsBucket, '/cupera-userimportjob*', ] ] Condition: ArnLike: aws:SourceArn: - Fn::Join: [ '', [ 'arn:', !Ref AWS::Partition, ':s3:::', !Ref UserImportJobMappingFiles ] ] StringEquals: aws:SourceAccount: !Ref AWS::AccountId CheckExecutionsLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Returns whether a state machine has multiple executions running Handler: "workflow-common/check-state-machine-executions.handler" Role: !GetAtt CheckExecutionsLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-common.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] CheckExecutionsLambdaRole: Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: states:ListExecutions cannot have specific resources and requires "*". Type: AWS::IAM::Role Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: check-state-machine-executions-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - states:ListExecutions Resource: "*" ####################################### # These resources will be created in the primary region only ####################################### ExportWorkflowServiceRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Metadata: cfn_nag: rules_to_suppress: - id: W76 reason: SPCM for IAM policy document is higher than 25 - All permissions are required. Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - states.amazonaws.com Action: - sts:AssumeRole Path: /service-role/ Policies: - PolicyName: StepFunctionPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: lambda:InvokeFunction Resource: - !GetAtt ExportUsersLambda.Arn - !GetAtt ListGroupsLambda.Arn - !GetAtt ExportGroupLambda.Arn - !GetAtt ExportUsersInGroupLambda.Arn - !GetAtt CheckExecutionsLambda.Arn - !GetAtt CheckUserPoolConfigLambda.Arn - !GetAtt BackupTableCleanupLambda.Arn - !GetAtt WorkflowMessageBrokerLambda.Arn ExportWorkflow: Type: AWS::StepFunctions::StateMachine Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} RoleArn: !GetAtt ExportWorkflowServiceRole.Arn DefinitionString: !Sub | { "Comment": "SOLUTION_NAME_PLACEHOLDER VERSION_PLACEHOLDER: Backs up User Profiles, Groups and Group Memberships to a DynamoDB Global Table", "StartAt": "CheckExecutionsLambda", "States": { "CheckExecutionsLambda": { "Type": "Task", "Resource": "${CheckExecutionsLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "Only this execution running for this state machine?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Only this execution running for this state machine?": { "Type": "Choice", "Choices": [ { "Variable": "$.OnlyThisStateMachineExecution", "BooleanEquals": true, "Next": "Yes - only this execution is running" } ], "Default": "No - at least one other execution is running" }, "Yes - only this execution is running": { "Type": "Pass", "Next": "CheckUserPoolConfigLambda" }, "No - at least one other execution is running": { "Type": "Fail", "Error": "AnotherExecutionRunning", "Cause": "At least one other execution for this state machine was found to be running. Please check logs for CheckExecutionsLambda for details" }, "CheckUserPoolConfigLambda": { "Type": "Task", "Resource": "${CheckUserPoolConfigLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "ExportUsersLambda", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "ExportUsersLambda": { "Type": "Task", "Resource": "${ExportUsersLambda.Arn}", "InputPath": "$", "OutputPath": "$.result", "Next": "Has ListUsers Next Token?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Has ListUsers Next Token?": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.paginationToken", "StringEquals": "" }, "Next": "Yes - ListUsers" }, { "Variable": "$.paginationToken", "StringEquals": "", "Next": "No - ListUsers" } ], "Default": "No - ListUsers" }, "Yes - ListUsers": { "Type": "Pass", "Next": "Wait 1 sec - ListUsers" }, "Wait 1 sec - ListUsers": { "Comment": "A Wait state delays the state machine from continuing for a specified time.", "Type": "Wait", "Seconds": 1, "Next": "ExportUsersLambda" }, "No - ListUsers": { "Type": "Pass", "Next": "ListGroupsLambda" }, "ListGroupsLambda": { "Type": "Task", "Resource": "${ListGroupsLambda.Arn}", "InputPath": "$", "Next": "Process Groups Map", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "Process Groups Map": { "Type": "Map", "ResultPath": "$.ProcessedGroups", "ItemsPath": "$.Groups", "Parameters": { "groupName.$": "$$.Map.Item.Value.groupName", "groupDescription.$": "$$.Map.Item.Value.groupDescription", "groupPrecedence.$": "$$.Map.Item.Value.groupPrecedence", "groupLastModifiedDate.$": "$$.Map.Item.Value.groupLastModifiedDate", "UsernameAttributes.$": "$$.Map.Item.Value.UsernameAttributes", "exportTimestamp.$": "$.ExportTimestamp" }, "Iterator": { "StartAt": "ExportGroupLambda", "States": { "ExportGroupLambda": { "Type": "Task", "Resource": "${ExportGroupLambda.Arn}", "InputPath": "$", "Next": "ExportUsersInGroupLambda", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambdaGroupsMap" } ] }, "ExportUsersInGroupLambda": { "Type": "Task", "Resource": "${ExportUsersInGroupLambda.Arn}", "InputPath": "$", "Next": "Processed all users in group?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambdaGroupsMap" } ] }, "Processed all users in group?": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.processedAllUsersInGroup", "StringEquals": "Yes" }, "Next": "More users to process in group" }, { "Variable": "$.processedAllUsersInGroup", "StringEquals": "Yes", "Next": "All users in group processed" } ], "Default": "More users to process in group" }, "More users to process in group": { "Type": "Pass", "Next": "Wait 1 second" }, "Wait 1 second": { "Comment": "A Wait state delays the state machine from continuing for a specified time.", "Type": "Wait", "Seconds": 1, "Next": "ExportUsersInGroupLambda" }, "All users in group processed": { "Type": "Pass", "End": true }, "WorkflowErrorHandlerLambdaGroupsMap": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Unexpected workflow error in Groups map" }, "Unexpected workflow error in Groups map": { "Type": "Fail", "Error": "UnexpectedErrorInWorkflow", "Cause": "An unexpected error occured while executing this workflow. Please check logs for the State Machine task that generated this error for details" } } }, "MaxConcurrency": 1, "Next": "Processed all groups?" }, "Processed all groups?": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.ProcessedAllGroups", "StringEquals": "Yes" }, "Next": "More groups to process" }, { "Variable": "$.ProcessedAllGroups", "StringEquals": "Yes", "Next": "All groups processed" } ], "Default": "All groups processed" }, "More groups to process": { "Type": "Pass", "Next": "Wait 1 sec - List Groups" }, "Wait 1 sec - List Groups": { "Comment": "A Wait state delays the state machine from continuing for a specified time.", "Type": "Wait", "Seconds": 1, "Next": "ListGroupsLambda" }, "All groups processed": { "Type": "Pass", "Next": "BackupTableCleanup: Find Items" }, "BackupTableCleanup: Find Items": { "Type": "Task", "Resource": "${BackupTableCleanupLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "BackupTableCleanup: Checked Entire Table?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "BackupTableCleanup: Checked Entire Table?": { "Type": "Choice", "Choices": [ { "Not": { "Variable": "$.lastEvaluatedKey", "StringEquals": "" }, "Next": "BackupTableCleanup: No, not finished checking table" }, { "Variable": "$.lastEvaluatedKey", "StringEquals": "", "Next": "BackupTableCleanup: Yes, finished checking table" } ] }, "BackupTableCleanup: No, not finished checking table": { "Type": "Pass", "Next": "BackupTableCleanup: Find Items" }, "BackupTableCleanup: Yes, finished checking table": { "Type": "Pass", "Next": "BackupTableCleanup: Items to remove?" }, "BackupTableCleanup: Items to remove?": { "Type": "Choice", "Choices": [ { "Variable": "$.NumItemsAddedToQueue", "NumericGreaterThan": 0, "Next": "BackupTableCleanup: Remove Items" } ], "Default": "WorkflowCleanupLambda" }, "BackupTableCleanup: Remove Items": { "Type": "Task", "Resource": "${BackupTableCleanupLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "OutputPath": "$.result", "Next": "BackupTableCleanup: All items removed?", "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "BackupTableCleanup: All items removed?": { "Type": "Choice", "Choices": [ { "Variable": "$.IsQueueEmpty", "BooleanEquals": true, "Next": "BackupTableCleanup: Yes, all items have been removed" } ], "Default": "BackupTableCleanup: No, not all items have been removed" }, "BackupTableCleanup: No, not all items have been removed": { "Type": "Pass", "Next": "BackupTableCleanup: Remove Items" }, "BackupTableCleanup: Yes, all items have been removed": { "Type": "Pass", "Next": "WorkflowCleanupLambda" }, "WorkflowCleanupLambda": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "End": true, "Catch": [ { "ErrorEquals": [ "States.TaskFailed" ], "Next": "WorkflowErrorHandlerLambda" } ] }, "WorkflowErrorHandlerLambda": { "Type": "Task", "Resource": "${WorkflowMessageBrokerLambda.Arn}", "Parameters": { "Input.$": "$", "Context.$": "$$" }, "Next": "Unexpected workflow error" }, "Unexpected workflow error": { "Type": "Fail", "Error": "UnexpectedErrorInWorkflow", "Cause": "An unexpected error occured while executing this workflow. Please check logs for the State Machine task that generated this error for details" } } } ExportWorkflowEventRule: Type: AWS::Events::Rule Condition: IsPrimaryRegion Properties: Description: Starts a state machine for the Export Workflow of the Cognito User Profiles Export Reference Architecture ScheduleExpression: !If [ExportDaily, "rate(1 day)", !If [ExportWeekly, "rate(7 days)", "rate(30 days)"]] State: ENABLED Targets: - Id: export-workflow-event-rule-target RoleArn: !GetAtt ExportWorkflowEventRuleRole.Arn Arn: !Ref ExportWorkflow ExportWorkflowEventRuleRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: export-workflow-event-rule-policy PolicyDocument: Statement: - Effect: Allow Action: - states:StartExecution Resource: - !Ref ExportWorkflow ExportUsersLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Export user profiles from the Primary User Pool to the Backup Table Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], !Ref "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Handler: "workflow-export/export-users.handler" Role: !GetAtt ExportUsersLambdaRole.Arn Runtime: nodejs14.x Timeout: 900 MemorySize: 128 Environment: Variables: USER_POOL_ID: !GetAtt StackSetConstantsCustomResource.PrimaryUserPoolId TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName COGNITO_TPS: !Ref CognitoTPS TYPE_USER: !Join ["-", [ "USER", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ExportUsersLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/* - PolicyName: ExportUsersPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - cognito-idp:ListUsers - cognito-idp:AdminGetUser Resource: - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${StackSetConstantsCustomResource.PrimaryUserPoolId}" - Effect: Allow Action: - dynamodb:BatchWriteItem Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} ListGroupsLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Lists the groups in a Cognito User Pool Handler: "workflow-export/list-groups.handler" Role: !GetAtt ListGroupsLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: USER_POOL_ID: !GetAtt StackSetConstantsCustomResource.PrimaryUserPoolId COGNITO_TPS: !Ref CognitoTPS SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ListGroupsLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: list-groups-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - cognito-idp:ListGroups Resource: - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${StackSetConstantsCustomResource.PrimaryUserPoolId}" ExportGroupLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Exports the supplied group name to the backup table Handler: "workflow-export/export-group.handler" Role: !GetAtt ExportGroupLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName SOLUTION_INSTANCE_ID: !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID TYPE_GROUP: !Join ["-", [ "GROUP", !GetAtt StackSetConstantsCustomResource.SolutionInstanceUUID ] ] SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ExportGroupLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: export-users-in-group-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - dynamodb:PutItem Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} ExportUsersInGroupLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Exports users in the supplied group name to the backup table Handler: "workflow-export/export-users-in-group.handler" Role: !GetAtt ExportUsersInGroupLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: USER_POOL_ID: !GetAtt StackSetConstantsCustomResource.PrimaryUserPoolId BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName COGNITO_TPS: !Ref CognitoTPS SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] ExportUsersInGroupLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: export-users-in-group-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - cognito-idp:ListUsersInGroup Resource: - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${StackSetConstantsCustomResource.PrimaryUserPoolId}" - Effect: Allow Action: - dynamodb:BatchWriteItem Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName} CheckUserPoolConfigLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Returns whether a user pool's configuration is supported by this solution Handler: "workflow-export/check-user-pool-config.handler" Role: !GetAtt CheckUserPoolConfigLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: PRIMARY_USER_POOL_ID: !GetAtt StackSetConstantsCustomResource.PrimaryUserPoolId SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] CheckUserPoolConfigLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: check-user-pool-config-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* # - Effect: Allow # Action: # - cognito-idp:GetUserPoolMfaConfig # Resource: # - !Sub "arn:${AWS::Partition}:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${StackSetConstantsCustomResource.PrimaryUserPoolId}" - Effect: Allow Action: - cognito-idp:DescribeUserPool Resource: - !Sub arn:${AWS::Partition}:cognito-idp:${PrimaryRegion}:${AWS::AccountId}:userpool/${StackSetConstantsCustomResource.PrimaryUserPoolId} ExportWorkflowQueue: Type: AWS::SQS::Queue Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} QueueName: !Sub "${StackSetConstantsCustomResource.ExportWorkflowQueueNamePrefix}-${StackSetConstantsCustomResource.SolutionInstanceUUID}" KmsMasterKeyId: alias/aws/sqs VisibilityTimeout: 30 RedrivePolicy: deadLetterTargetArn: !GetAtt ExportWorkflowDeadLetterQueue.Arn maxReceiveCount: 2 ExportWorkflowDeadLetterQueue: Type: AWS::SQS::Queue Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} QueueName: !Sub "${StackSetConstantsCustomResource.ExportWorkflowQueueNamePrefix}-${StackSetConstantsCustomResource.SolutionInstanceUUID}-dlq" KmsMasterKeyId: alias/aws/sqs VisibilityTimeout: 30 BackupTableCleanupLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: VPC for Lambda is not needed. This serverless architecture does not deploy a VPC. - id: W92 reason: ReservedConcurrentExecutions is not needed for this Lambda function. Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} Description: Removes items from the Backup Table that were not updated during the export workflow Handler: "workflow-export/backup-table-cleanup.handler" Role: !GetAtt BackupTableCleanupLambdaRole.Arn Code: S3Bucket: !Join ["-", [!FindInMap ["Solution", "Config", "S3BucketPrefix"], Ref: "AWS::Region"]] S3Key: !Join ["/", [!FindInMap ["Solution", "Config", "S3KeyPrefix"], "workflow-export.zip"]] Runtime: nodejs14.x Timeout: 300 Environment: Variables: BACKUP_TABLE_NAME: !GetAtt StackSetConstantsCustomResource.BackupTableName QUEUE_URL: !Ref ExportWorkflowQueue SOLUTION_ID: !FindInMap ["Solution", "Config", "SolutionId"] SOLUTION_VERSION: !FindInMap ["Solution", "Config", "Version"] BackupTableCleanupLambdaRole: Type: AWS::IAM::Role Condition: IsPrimaryRegion Properties: Tags: - Key: solution-id-SO0126 Value: !Sub ${StackSetConstantsCustomResource.ParentStackName}-${PrimaryRegion} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: backup-table-cleanup-policy PolicyDocument: Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/* - Effect: Allow Action: - sqs:SendMessage - sqs:SendMessageBatch - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:GetQueueAttributes Resource: - !GetAtt ExportWorkflowQueue.Arn - Effect: Allow Action: - dynamodb:Scan - dynamodb:BatchWriteItem Resource: - !Sub arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${StackSetConstantsCustomResource.BackupTableName}