--- AWSTemplateFormatVersion: '2010-09-09' Description: AWS CloudFormation template to create a Cloud9 environment for AWS Quant Infra Parameters: C9InstanceType: Description: Cloud9 instance type Type: String Default: m5.large AllowedValues: - t3.small - t3.medium - m4.large - m5.large ConstraintDescription: Must be a valid Cloud9 instance type Resources: ################## PERMISSIONS AND ROLES ################# C9Role: Type: AWS::IAM::Role Properties: Tags: - Key: Environment Value: AWS Example AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess Path: "/" C9LambdaExecutionRole: 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: - '' - - C9LambdaPolicy- - 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:DescribeVolumes - ec2:AssociateIamInstanceProfile - ec2:ModifyInstanceAttribute - ec2:ModifyVolume - ec2:ReplaceIamInstanceProfileAssociation - iam:ListInstanceProfiles - iam:PassRole Resource: "*" ################## LAMBDA BOOTSTRAP FUNCTION ################ C9BootstrapInstanceLambda: Description: Bootstrap Cloud9 instance Type: Custom::C9BootstrapInstanceLambda DependsOn: - C9BootstrapInstanceLambdaFunction - C9Instance - C9LambdaExecutionRole Properties: Tags: - Key: Environment Value: AWS Example ServiceToken: Fn::GetAtt: - C9BootstrapInstanceLambdaFunction - Arn REGION: Ref: AWS::Region StackName: Ref: AWS::StackName EnvironmentId: Ref: C9Instance LabIdeInstanceProfileArn: !GetAtt C9InstanceProfile.Arn C9BootstrapInstanceLambdaFunction: Type: AWS::Lambda::Function Properties: Tags: - Key: Environment Value: AWS Example Handler: index.lambda_handler Role: Fn::GetAtt: - C9LambdaExecutionRole - Arn Runtime: python3.7 MemorySize: 256 Timeout: '600' Code: ZipFile: | from __future__ import print_function import boto3 import json import os import time import traceback import cfnresponse def lambda_handler(event, context): print(f'event: {event}') print(f'context: {context}') responseData = {} if event['RequestType'] == 'Delete': try: responseData = {'Success': 'Finished cleanup'} cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID') except Exception as e: responseData = {'Error': traceback.format_exc(e)} cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID') if event['RequestType'] == 'Create': try: # Open AWS clients ec2 = boto3.client('ec2') # Get the InstanceId from Cloud9 IDE print('tag:aws:cloud9:environment : {}'.format(event['ResourceProperties']['EnvironmentId'])) instance = ec2.describe_instances(Filters=[{'Name': 'tag:aws:cloud9:environment','Values': [event['ResourceProperties']['EnvironmentId']]}])['Reservations'][0]['Instances'][0] print(f'instance: {instance}') volume_id = instance['BlockDeviceMappings'][0]['Ebs']['VolumeId'] print(f'Volume Id {volume_id}') ec2.modify_volume(VolumeId=volume_id, Size=25) print('Changed Volume to 25GB') # Create the IamInstanceProfile request object iam_instance_profile = { 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'] } print(f'iam_instance_profile: {iam_instance_profile}') # Wait for Instance to become ready before adding Role instance_state = instance['State']['Name'] while instance_state != 'running': time.sleep(5) instance_state = ec2.describe_instances(InstanceIds=[instance['InstanceId']]) print(f'waiting for the instance state to be "running", current instance_state: {instance_state}') # attach instance profile print(f'Instance is running , about to associate iam_instance_profile: {iam_instance_profile}') response = ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId']) print(f'response - associate_iam_instance_profile: {response}') r_ec2 = boto3.resource('ec2') responseData = {'Success': 'Started bootstrapping for instance: '+instance['InstanceId']} cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID') except Exception as e: responseData = {'Error': traceback.format_exc(e)} cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID') ################## SSM BOOTSRAP HANDLER ############### C9OutputBucket: Type: AWS::S3::Bucket DeletionPolicy: Delete C9SSMDocument: Type: AWS::SSM::Document Properties: Tags: - Key: Environment Value: AWS Example Content: Yaml DocumentType: Command Content: schemaVersion: '2.2' description: Bootstrap Cloud9 Instance mainSteps: - action: aws:runShellScript name: C9bootstrap inputs: runCommand: - "#!/bin/bash" - date - echo '=== Install deps ===' - sudo yum -y install jq - echo '=== Update to the latest AWS CLI ===' - sudo -H -u ec2-user aws --version - curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" - unzip awscliv2.zip - sudo ./aws/install - . /home/ec2-user/.bash_profile - sudo -H -u ec2-user aws --version - echo '=== setup AWS configs ===' - rm -vf /home/ec2-user/.aws/credentials - export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) - export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region') - echo "export ACCOUNT_ID=${ACCOUNT_ID}" >> /home/ec2-user/.bash_profile - echo "export AWS_REGION=${AWS_REGION}" >> /home/ec2-user/.bash_profile - sudo -H -u ec2-user aws configure set default.region ${AWS_REGION} - sudo -H -u ec2-user aws configure get default.region - sudo -H -u ec2-user aws sts get-caller-identity - echo "Bootstrap completed with return code $?" C9BootstrapAssociation: Type: AWS::SSM::Association DependsOn: - C9OutputBucket Properties: Name: !Ref C9SSMDocument OutputLocation: S3Location: OutputS3BucketName: !Ref C9OutputBucket OutputS3KeyPrefix: bootstrapoutput Targets: - Key: tag:SSMBootstrap Values: - Active ################## INSTANCE ##################### C9InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - Ref: C9Role C9Instance: Description: "AWS Quant Deployment Space" DependsOn: C9BootstrapAssociation Type: AWS::Cloud9::EnvironmentEC2 Properties: Description: AWS Cloud9 instance for AWS Quant Deployment AutomaticStopTimeMinutes: 3600 InstanceType: Ref: C9InstanceType Name: Ref: AWS::StackName Repositories: - PathComponent: /aws-quant-deployment RepositoryUrl: https://github.com/aws-samples/asynchronous-messaging-workshop.git Tags: - Key: SSMBootstrap Value: Active - Key: Environment Value: Ref: AWS::StackName Outputs: Cloud9IDE: Value: Fn::Join: - '' - - https:// - Ref: AWS::Region - ".console.aws.amazon.com/cloud9/ide/" - Ref: C9Instance - "?region=" - Ref: AWS::Region