--- AWSTemplateFormatVersion: '2010-09-09' Description: 'Orchestrating an Application Process with AWS Batch in Fargate using CloudFormation' Parameters: StackName: Type: String #Default: batch-processing-job Description: The name of the application stack Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: true EnableDnsHostnames: true InternetGateway: Type: AWS::EC2::InternetGateway PublicSubnetRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: VPC VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: Ref: VPC InternetGatewayId: Ref: InternetGateway NATEIP: DependsOn: VPCGatewayAttachment Type: AWS::EC2::EIP Properties: Domain: vpc NATGateway: DependsOn: VPCGatewayAttachment Type: AWS::EC2::NatGateway Properties: AllocationId: Fn::GetAtt: - NATEIP - AllocationId SubnetId: Ref: PublicSubnet SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: EC2 Security Group for instances launched in the VPC by Batch VpcId: Ref: VPC PublicSubnet: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.0.0.0/24 VpcId: Ref: VPC MapPublicIpOnLaunch: 'True' Tags: - Key: Name Value: !Sub ${StackName} Public Subnet PrivateSubnet: Type: AWS::EC2::Subnet Properties: CidrBlock: 10.0.1.0/24 VpcId: Ref: VPC Tags: - Key: Name Value: !Sub ${StackName} Private Subnet PublicSubnetRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: VPC Tags: - Key: Name Value: !Sub ${StackName} Public Route - Key: Project Value: AWS Batch in Fargate PublicSubnetRoute: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: Ref: PublicSubnetRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: InternetGateway PublicSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: Ref: PublicSubnet RouteTableId: Ref: PublicSubnetRouteTable PrivateSubnetRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: VPC Tags: - Key: Name Value: !Sub ${StackName} Private Route - Key: Project Value: AWS Batch in Fargate PrivateSubnetRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref 'PrivateSubnetRouteTable' DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: Ref: NATGateway PrivateSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: Ref: PrivateSubnet RouteTableId: Ref: PrivateSubnetRouteTable LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: Fn::Sub: lambda-role AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Version: 2012-10-17 ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSLambdaExecute - arn:aws:iam::aws:policy/AWSBatchFullAccess - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole Path: / Policies: - PolicyName: !Sub ${StackName}-lambda-s3-get-policy PolicyDocument: Statement: - Effect: Allow Action: - 's3:Get*' - 's3:List*' Resource: '*' Resource: !Join - '' - - 'arn:aws:s3:::' - !Sub "${StackName}-${AWS::AccountId}" - /* BatchServiceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: batch.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole BatchProcessingJobDefinition: Type: AWS::Batch::JobDefinition Properties: Type: container PropagateTags: true JobDefinitionName: BatchJobDefinition ContainerProperties: Image: Fn::Join: - '' - - Ref: AWS::AccountId - .dkr.ecr. - Ref: AWS::Region - !Sub '.amazonaws.com/${StackName}-repository:latest' FargatePlatformConfiguration: PlatformVersion: LATEST ResourceRequirements: - Value: 0.25 Type: VCPU - Value: 512 Type: MEMORY JobRoleArn: !GetAtt 'BatchTaskExecutionRole.Arn' ExecutionRoleArn: !GetAtt 'BatchTaskExecutionRole.Arn' LogConfiguration: LogDriver: awslogs Options: awslogs-group: !Ref 'BatchLogGroup' awslogs-region: !Ref AWS::Region awslogs-stream-prefix: !Sub ${StackName}-logs Command: - python - batch_processor.py PlatformCapabilities: - FARGATE Tags: Service: Batch Name: JobDefinitionTag Expected: MergeTag BatchLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub ${StackName}-awslogs RetentionInDays: 7 BatchTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${StackName}-taskexec-role AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: [ecs-tasks.amazonaws.com] Action: ['sts:AssumeRole'] Path: / Policies: - PolicyName: AmazonECSTaskExecutionRolePolicy PolicyDocument: Statement: - Effect: Allow Action: - 'ecr:GetAuthorizationToken' - 'ecr:BatchCheckLayerAvailability' - 'ecr:GetDownloadUrlForLayer' - 'ecr:BatchGetImage' - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: '*' - PolicyName: !Sub ${StackName}-ecs-task-s3-get-policy PolicyDocument: Statement: - Effect: Allow Action: - s3:PutObject - s3:GetObject - s3:ListBucket Resource: - !Join - '' - - 'arn:aws:s3:::' - !Sub "${StackName}-${AWS::AccountId}" - !Join - '' - - 'arn:aws:s3:::' - !Sub "${StackName}-${AWS::AccountId}" - /* - PolicyName: !Sub ${StackName}-ecs-task-dynamo-policy PolicyDocument: Statement: - Effect: Allow Action: - 'dynamodb:PutItem' Resource: !Join - '' - - 'arn:aws:dynamodb:' - !Sub "${AWS::Region}:${AWS::AccountId}:table/${StackName}" BatchProcessingJobQueue: Type: AWS::Batch::JobQueue Properties: JobQueueName: !Sub "${StackName}-queue" State: ENABLED Priority: 1 ComputeEnvironmentOrder: - Order: 1 ComputeEnvironment: Ref: ComputeEnvironment ComputeEnvironment: Type: AWS::Batch::ComputeEnvironment Properties: Type: MANAGED State: ENABLED ComputeResources: Type: FARGATE MaxvCpus: 40 Subnets: - Ref: PrivateSubnet SecurityGroupIds: - Ref: SecurityGroup ServiceRole: Ref: BatchServiceRole BatchProcessS3Bucket: Type: AWS::S3::Bucket DependsOn: BatchProcessBucketPermission Properties: BucketName: !Sub '${StackName}-${AWS::AccountId}' NotificationConfiguration: LambdaConfigurations: - Event: 's3:ObjectCreated:*' Function: !GetAtt BatchProcessingLambdaInvokeFunction.Arn BatchProcessBucketPermission: Type: AWS::Lambda::Permission Properties: Action: 'lambda:InvokeFunction' FunctionName: !Ref BatchProcessingLambdaInvokeFunction Principal: s3.amazonaws.com SourceAccount: !Ref "AWS::AccountId" SourceArn: !Sub "arn:aws:s3:::${StackName}-${AWS::AccountId}" BatchProcessingLambdaInvokeFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub "${StackName}-lambda" Description: Python Function Handler that would be triggered BY s3 events TO the aws batch Handler: index.lambda_handler Runtime: python3.9 MemorySize: 128 Timeout: 30 Environment: Variables: BATCH_JOB_QUEUE_NAME: !Sub "${StackName}-queue" DYNAMO_TABLE_NAME: !Sub ${StackName} Role: Fn::GetAtt: - LambdaExecutionRole - Arn Code: ZipFile: | import os import json import boto3 def lambda_handler(event, context): inputFileName = "" bucketName = "" for record in event['Records']: bucketName = record['s3']['bucket']['name'] inputFileName = record['s3']['object']['key'] response = { 'statusCode': 200, 'body': json.dumps('Input Received - ' + json.dumps(event)) } batch = boto3.client('batch') region = batch.meta.region_name batch_job_queue_name = os.environ["BATCH_JOB_QUEUE_NAME"] db_table_name = os.environ["DYNAMO_TABLE_NAME"] batchCommand = "--bucketName " + bucketName + " --fileName " + inputFileName + " --region " + region + " --dbTableName " + db_table_name out = "inputFileName - " + bucketName + "/" + inputFileName + " Region " + region + " DBTableName " + db_table_name out = out + " " + batchCommand print(out) response = batch.submit_job(jobName=batch_job_queue_name, jobQueue=batch_job_queue_name, jobDefinition='BatchJobDefinition', containerOverrides={ "command": [ "python", "batch_processor.py", batchCommand ], "environment": [ {"name": "InputBucket", "value": bucketName}, {"name": "FileName", "value": inputFileName}, {"name": "Region", "value": region}, {"name": "DBTableName", "value": db_table_name} ] }) print("Job ID is {}.".format(response['jobId'])) return response #Code Commit CodeCommitRepository: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Sub ${StackName}-repo RepositoryDescription: Respository to maintain code related to the Batch Processing Jobs. #Code Build CodeBuildProject: Type: AWS::CodeBuild::Project Properties: Name: !Sub ${StackName}-build Description: Batch processing application codebuild project. ServiceRole: !GetAtt CodeBuildRole.Arn Artifacts: Type: no_artifacts Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:1.0 PrivilegedMode: true EnvironmentVariables: - Name: REPOSITORY_URI Type: PLAINTEXT Value: Fn::Join: - '' - - Ref: AWS::AccountId - .dkr.ecr. - Ref: AWS::Region - !Sub '.amazonaws.com/${StackName}-repository:latest' - Name: AWS_DEFAULT_REGION Type: PLAINTEXT Value: Ref: AWS::Region Source: BuildSpec: config/buildspec.yml Location: Fn::Join: - '' - - 'https://git-codecommit.' - Ref: AWS::Region - '.amazonaws.com/v1/repos/' - !Sub ${StackName}-repo Type: CODECOMMIT SourceVersion: refs/heads/master TimeoutInMinutes: 10 CodeBuildRole: Type: AWS::IAM::Role Properties: ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess - arn:aws:iam::aws:policy/AWSCodeCommitFullAccess AssumeRolePolicyDocument: Statement: - Action: ['sts:AssumeRole'] Effect: Allow Principal: Service: [codebuild.amazonaws.com] Version: '2012-10-17' Path: / Policies: - PolicyName: CodeBuildAccess PolicyDocument: Version: '2012-10-17' Statement: - Action: - 'logs:*' - 'ec2:CreateNetworkInterface' - 'ec2:DescribeNetworkInterfaces' - 'ec2:DeleteNetworkInterface' - 'ec2:DescribeSubnets' - 'ec2:DescribeSecurityGroups' - 'ec2:DescribeDhcpOptions' - 'ec2:DescribeVpcs' - 'ec2:CreateNetworkInterfacePermission' Effect: Allow Resource: '*' # CloudWatchEvents Code build Rold CloudWatchEventsCodeBuildRole: Type: AWS::IAM::Role Properties: RoleName: !Sub ${StackName}-cw-events-codebuild-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: aws-events-code-build PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'codebuild:StartBuild' Resource: !GetAtt CodeBuildProject.Arn # CloudWatch Event Rule for codecommit build trigger CloudWatchEventCodeBuildEventRule: Type: AWS::Events::Rule Properties: Description: "This event rule triggers the build on code commit event" EventPattern: source: - "aws.codecommit" detail-type: - "CodeCommit Repository State Change" detail: event: - "referenceCreated" - "referenceUpdated" referenceType: - "branch" referenceName: - "master" State: "ENABLED" Targets: - Arn: {'Fn::GetAtt': [CodeBuildProject, Arn]} Id: cloudwatch-codebuild-eventrules RoleArn: !GetAtt CloudWatchEventsCodeBuildRole.Arn BatchProcessRepository: Type: AWS::ECR::Repository Properties: RepositoryName: !Sub ${StackName}-repository BatchProcessingDynamoDBTable: Type: AWS::DynamoDB::Table Properties: TableName: !Sub ${StackName} AttributeDefinitions: - AttributeName: "ProductId" AttributeType: "S" - AttributeName: "ProductName" AttributeType: "S" - AttributeName: "CreatedTime" AttributeType: "S" KeySchema: - AttributeName: "ProductId" KeyType: "HASH" - AttributeName: "ProductName" KeyType: "RANGE" GlobalSecondaryIndexes: - IndexName: "GSI" KeySchema: - AttributeName: "CreatedTime" KeyType: "HASH" Projection: ProjectionType: "KEYS_ONLY" ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 #### VPC Endpoints S3VPCEndpoint: Type: "AWS::EC2::VPCEndpoint" Properties: VpcEndpointType: Interface ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3" VpcId: !Ref VPC PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: - 's3:*' Resource: - !Join - '' - - 'arn:aws:s3:::' - !Sub "${StackName}-${AWS::AccountId}" DynamoDBVPCEndpoint: Type: "AWS::EC2::VPCEndpoint" Properties: RouteTableIds: - !Ref PrivateSubnetRouteTable ServiceName: !Sub "com.amazonaws.${AWS::Region}.dynamodb" VpcId: !Ref VPC PolicyDocument: Statement: - Effect: Allow Principal: '*' Action: - 'dynamodb:*' Resource: !Join - '' - - 'arn:aws:dynamodb:' - !Sub "${AWS::Region}:${AWS::AccountId}:table/${StackName}" EcrApiVPCEndpoint: Type: "AWS::EC2::VPCEndpoint" Properties: VpcEndpointType: Interface ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api" VpcId: !Ref VPC PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: - 'ecr:*' Resource: - !Join - '' - - 'arn:aws:ecr:' - !Sub "${AWS::Region}:${AWS::AccountId}:repository/${StackName}" EcrDkrVPCEndpoint: Type: "AWS::EC2::VPCEndpoint" Properties: VpcEndpointType: Interface ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr" VpcId: !Ref VPC PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: - 'ecr:*' Resource: - !Join - '' - - 'arn:aws:ecr:' - !Sub "${AWS::Region}:${AWS::AccountId}:repository/${StackName}" Outputs: ComputeEnvironmentArn: Value: Ref: ComputeEnvironment BatchProcessingJobQueueArn: Value: Ref: BatchProcessingJobQueue BatchProcessingJobDefinitionArn: Value: Ref: BatchProcessingJobDefinition BucketName: Value: Ref: BatchProcessS3Bucket LambdaName: Value: Ref: BatchProcessingLambdaInvokeFunction