--- AWSTemplateFormatVersion: '2010-09-09' Description: | (SO9052) - AWS CloudFormation template for dynamic AWS Cloud9 setup required for the Intelligent Document Processing Workshop. Parameters: WorkshopInstanceType: Description: Workshop Cloud9 instance type - Constrained AZs in a region could cause deployment failure for certain instance types Type: String Default: m5.large AllowedValues: - t3.large - t3.medium - c5.large - t2.large - t2.medium - m4.large - t2.xlarge - m5.large - m5.xlarge - c5.xlarge - c4.xlarge ConstraintDescription: Must be a valid Cloud9 instance type WorkshopInstanceVolumeSize: Type: Number Description: The Size in GB of the Cloud9 Instance Volume. Default: 50 S3URIDocumentDownload: Type: String Description: The S3 URI for the documents to download Default: s3://idp-assets-wwso/workshop-data/docs.tar.gz Resources: ################## PERMISSIONS AND ROLES ################# WorkshopRole: Type: AWS::IAM::Role Properties: Tags: - Key: Environment Value: AWS Workshop AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com - cloud9.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess - arn:aws:iam::aws:policy/AWSCloud9SSMInstanceProfile Path: "/service-role/" WorkshopLambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" Policies: - PolicyName: Fn::Join: - '' - - WorkshopLambdaPolicy- - Ref: AWS::Region PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - cloudformation:DescribeStacks - cloudformation:DescribeStackEvents - cloudformation:DescribeStackResource - cloudformation:DescribeStackResources - ec2:DescribeInstances - ec2:AssociateIamInstanceProfile - ec2:ModifyInstanceAttribute - ec2:ReplaceIamInstanceProfileAssociation - iam:ListInstanceProfiles - iam:PassRole - s3:ListBucket - s3:DeleteObject - s3:DeleteObjectVersion Resource: "*" ################## LAMBDA BOOTSTRAP FUNCTION ################ WorkshopBootstrapInstanceLambda: Description: Bootstrap Cloud9 instance Type: Custom::WorkshopBootstrapInstanceLambda DependsOn: - WorkshopBootstrapInstanceLambdaFunction - WorkshopInstance - WorkshopInstanceProfile - WorkshopLambdaExecutionRole - WorkshopOutputBucket Properties: Tags: - Key: Environment Value: AWS Workshop ServiceToken: Fn::GetAtt: - WorkshopBootstrapInstanceLambdaFunction - Arn REGION: Ref: AWS::Region StackName: Ref: AWS::StackName EnvironmentId: Ref: WorkshopInstance LabIdeInstanceProfileName: Ref: WorkshopInstanceProfile LabIdeInstanceProfileArn: Fn::GetAtt: - WorkshopInstanceProfile - Arn WorkshopBucketId: Ref: WorkshopOutputBucket WorkshopBootstrapInstanceLambdaFunction: Type: AWS::Lambda::Function Properties: Tags: - Key: Environment Value: AWS Workshop Handler: index.lambda_handler Role: Fn::GetAtt: - WorkshopLambdaExecutionRole - Arn Runtime: python3.9 MemorySize: 256 Timeout: '600' Code: ZipFile: | import boto3 import traceback import cfnresponse import logging logger = logging.getLogger() logger.setLevel(logging.INFO) def lambda_handler(event, context): try: logger.info('event: {}'.format(event)) # Steps to do on Cloudformation Delete Request if event['RequestType'] == 'Delete': logger.info('Received Delete Request') # Empty SSM output bucket on deletion of stack bucket = event['ResourceProperties']['WorkshopBucketId'] logger.info('Deleting contents of ' + bucket) s3 = boto3.resource('s3') bucket = s3.Bucket(bucket) for obj in bucket.objects.filter(): s3.Object(bucket.name, obj.key).delete() # Steps to do on Cloudformation Update requests if event['RequestType'] == 'Update': logger.info('Received Update Request') # Steps to do on Cloudformation Create requests if event['RequestType'] == 'Create': # Open AWS clients ec2 = boto3.client('ec2') # Get ec2.instancesCollection Information of the Cloud9 IDE Environment instances = boto3.resource('ec2').instances.filter(Filters=[{'Name': 'tag:Name','Values': ['*' + event['ResourceProperties']['EnvironmentId']]}]) # Create the IamInstanceProfile request object iam_instance_profile = { 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'], 'Name': event['ResourceProperties']['LabIdeInstanceProfileName'] } # attach instance profile for instance in instances: response = ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance.id) logger.info('Received Create Request - Completed for : ' + instance.id) # Catch any exceptions except Exception as e: logger.error(e) traceback.format_exc() # Always respond back to Cloudformation so it isn't in excessive pending state finally: cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) ################## SSM BOOTSRAP HANDLER ############### WorkshopOutputBucket: Type: AWS::S3::Bucket DeletionPolicy: Delete WorkshopSSMDocument: Type: AWS::SSM::Document Properties: Tags: - Key: Environment Value: AWS Workshop DocumentType: Command Content: schemaVersion: '2.2' description: Bootstrap Cloud9 Instance mainSteps: - action: aws:runShellScript name: Workshopbootstrap inputs: timeoutSeconds: '900' runCommand: - "#!/bin/bash" - date - echo LANG=en_US.utf-8 >> /etc/environment - echo LC_ALL=en_US.UTF-8 >> /etc/environment ### Remove old awscli and install newest aws cli from pip, packaged version of awscli does not have switch to disable managed temporary credentials ### - echo '=== INSTALL and CONFIGURE default software components ===' - yum -y remove awscli; yum -y install jq - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - unzip awscliv2.zip - ./aws/install ### Setup the repo - su - ec2-user -c "cd /home/ec2-user/environment ; git clone https://github.com/aws-samples/aws-ai-intelligent-document-processing" - su - ec2-user -c "cd /home/ec2-user/environment ; mv ./aws-ai-intelligent-document-processing/idp-document-processing-at-scale ./" - su - ec2-user -c "cd /home/ec2-user/environment ; rm -rf ./aws-ai-intelligent-document-processing" - su - ec2-user -c "cd /home/ec2-user/environment/idp-document-processing-at-scale/ ; mkdir samples" - su - ec2-user -c "cd /home/ec2-user/environment/idp-document-processing-at-scale/ ; python3 -m pip install -r requirements.txt" - su - ec2-user -c "cd /home/ec2-user/environment/idp-document-processing-at-scale/ ; cdk bootstrap" ### Download and extract documents - !Sub su - ec2-user -c "cd /home/ec2-user/environment/idp-document-processing-at-scale/; aws s3 cp ${S3URIDocumentDownload} docs.tar.gz; tar xzvf docs.tar.gz -C ./samples" WorkshopBootstrapAssociation: Type: AWS::SSM::Association DependsOn: - WorkshopOutputBucket - WorkshopInstance - WorkshopBootstrapInstanceLambda Properties: Name: !Ref WorkshopSSMDocument OutputLocation: S3Location: OutputS3BucketName: !Ref WorkshopOutputBucket OutputS3KeyPrefix: bootstrapoutput Targets: - Key: tag:aws:cloud9:environment Values: - Ref: WorkshopInstance ################## INSTANCE ##################### WorkshopInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - Ref: WorkshopRole WorkshopInstance: Description: "-" Type: AWS::Cloud9::EnvironmentEC2 Properties: Description: AWS Cloud9 instance for Workshops AutomaticStopTimeMinutes: 180 ImageId: amazonlinux-2-x86_64 InstanceType: Ref: WorkshopInstanceType Name: AWS-IDP-Workshop Tags: - Key: Environment Value: AWS Workshop Outputs: Cloud9IDE: Value: Fn::Join: - '' - - https:// - Ref: AWS::Region - ".console.aws.amazon.com/cloud9/ide/" - Ref: WorkshopInstance - "?region=" - Ref: AWS::Region