AWSTemplateFormatVersion: '2010-09-09' Description: 'DynamoDB Workshop CloudFormation Template: Create an Amazon EC2 instance running the Amazon Linux with the applications required for running the DynamoDB workshop' Parameters: WorkshopCodeURL: Description: "The python script package, zip'd, with the workshop code." Default: "https://amazon-dynamodb-labs.com/assets/workshop.zip" Type: String InstanceType: Description: Workshop EC2 Instance type Type: String Default: t3.xlarge AllowedValues: - t3.xlarge ConstraintDescription: Must be a valid EC2 instance type VPCSelection: Description: This setting will control if the DesignPatterns server will launch in the default VPC or if a new VPC will be created, or if you wish to launch into an existing non-default VPC. Type: String Default: CreateNewVPC AllowedValues: - Default - CreateNewVPC ConstraintDescription: must be either Default or CreateNewVPC Conditions: DefaultVPC: Fn::Equals: - !Ref VPCSelection - Default CreateNewVPC: Fn::Equals: - !Ref VPCSelection - CreateNewVPC Mappings: AmazonLinuxAMI: us-east-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0b46d3491dbc6c19e GUID: 6f7652f821ac us-east-2: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-045ded0b933b76edd GUID: 6f7652f821ac us-west-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-098679a260e1209d1 GUID: 6f7652f821ac us-west-2: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0bd66fad2c6056e19 GUID: 6f7652f821ac eu-west-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-04fb47915aaea1e62 GUID: 6f7652f821ac eu-west-2: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0b71de60f601e6897 GUID: 6f7652f821ac eu-central-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-08d125a524c37d22e GUID: 6f7652f821ac sa-east-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-008dcd30fab4ab54d GUID: 6f7652f821ac ap-south-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0bfcf5aa671f35822 GUID: 6f7652f821ac ap-southeast-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0253cac8b82dc7263 GUID: 6f7652f821ac ap-southeast-2: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0e00766c034642337 GUID: 6f7652f821ac ap-northeast-1: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-00238cb37d54b2a4c GUID: 6f7652f821ac ap-northeast-2: # Name: amzn-ami-hvm-2018.03.0.20200904.0-x86_64-ebs AMI: ami-0ddea77382fd34b61 GUID: 6f7652f821ac DesignPatterns: options: UserDataURL: "https://amazon-dynamodb-labs.com/assets/UserData.sh" Resources: DDBReplicationRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:DescribeStream - dynamodb:GetRecords - dynamodb:GetShardIterator - dynamodb:ListStreams Resource: - '*' - Effect: Allow Action: - dynamodb:DeleteItem - dynamodb:PutItem Resource: - '*' - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - '*' DDBInstanceRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:Describe* - dynamodb:List* - dynamodb:Scan - dynamodb:Query - dynamodb:PutItem - dynamodb:DeleteItem - dynamodb:CreateTable - dynamodb:UpdateTable - dynamodb:BatchWriteItem - dynamodb:BatchGetItem - dynamodb:UpdateItem - dynamodb:TagResource - iam:GetRole - iam:PassRole - lambda:CreateFunction - lambda:CreateEventSourceMapping - ssm:UpdateInstanceInformation Resource: - '*' RootInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: "/" Roles: - !Ref DDBInstanceRole WorkshopWaitHandle: Type: AWS::CloudFormation::WaitConditionHandle WorkshopWaitCondition: Type: AWS::CloudFormation::WaitCondition Properties: Count: 1 Handle: !Ref 'WorkshopWaitHandle' Timeout: '600' WorkshopEC2Instance: Type: AWS::EC2::Instance Condition: DefaultVPC Properties: ImageId: !FindInMap - AmazonLinuxAMI - !Ref 'AWS::Region' - AMI InstanceType: !Ref 'InstanceType' IamInstanceProfile: !Ref 'RootInstanceProfile' SecurityGroups: - !Ref 'WorkshopSecurityGroup' Tags: - Key: Name Value: !Ref 'AWS::StackName' UserData: Fn::Base64: Fn::Sub: - | Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config locale: en_US.utf-8 --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.sh" #!/bin/bash echo [`date -u +"%Y-%m-%dT%H:%M:%SZ"`] Started Design Patterns User Data set -x function sleep_delay { if (( $SLEEP_TIME < $SLEEP_TIME_MAX )); then echo Sleeping $SLEEP_TIME sleep $SLEEP_TIME SLEEP_TIME=$(($SLEEP_TIME * 2)) else echo Sleeping $SLEEP_TIME_MAX sleep $SLEEP_TIME_MAX fi } # Executing bootstrap script SLEEP_TIME=10 SLEEP_TIME_MAX=3600 while true; do curl "${SUB_USERDATA_URL}" > /tmp/designpatternsbootstrap.sh RESULT=$? if [[ "$RESULT" -ne 0 ]]; then sleep_delay else /bin/bash /tmp/designpatternsbootstrap.sh ${SUB_GUID} "${WorkshopCodeURL}" ${AWS::Region} ${AWS::AccountId} "${SUB_REPL_ROLE}" "${WorkshopWaitHandle}" && exit 0 fi done --// - { SUB_USERDATA_URL: !FindInMap [DesignPatterns, options, UserDataURL], SUB_GUID: !FindInMap [AmazonLinuxAMI, !Ref "AWS::Region", GUID], SUB_REPL_ROLE: !GetAtt ['DDBReplicationRole', 'Arn'], } WorkshopEC2InstanceCustomVPC: Type: AWS::EC2::Instance Condition: CreateNewVPC Properties: ImageId: !FindInMap - AmazonLinuxAMI - !Ref 'AWS::Region' - AMI InstanceType: !Ref 'InstanceType' IamInstanceProfile: !Ref 'RootInstanceProfile' SubnetId: !Ref 'VPCSubnet' SecurityGroupIds: - Fn::GetAtt: ['WorkshopSecurityGroup', GroupId] Tags: - Key: Name Value: !Ref 'AWS::StackName' UserData: Fn::Base64: Fn::Sub: - | Content-Type: multipart/mixed; boundary="//" MIME-Version: 1.0 --// Content-Type: text/cloud-config; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="cloud-config.txt" #cloud-config locale: en_US.utf-8 --// Content-Type: text/x-shellscript; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="userdata.sh" #!/bin/bash echo [`date -u +"%Y-%m-%dT%H:%M:%SZ"`] Started Design Patterns User Data set -x function sleep_delay { if (( $SLEEP_TIME < $SLEEP_TIME_MAX )); then echo Sleeping $SLEEP_TIME sleep $SLEEP_TIME SLEEP_TIME=$(($SLEEP_TIME * 2)) else echo Sleeping $SLEEP_TIME_MAX sleep $SLEEP_TIME_MAX fi } # Executing bootstrap script SLEEP_TIME=10 SLEEP_TIME_MAX=3600 while true; do curl "${SUB_USERDATA_URL}" > /tmp/designpatternsbootstrap.sh RESULT=$? if [[ "$RESULT" -ne 0 ]]; then sleep_delay else /bin/bash /tmp/designpatternsbootstrap.sh ${SUB_GUID} "${WorkshopCodeURL}" ${AWS::Region} ${AWS::AccountId} "${SUB_REPL_ROLE}" "${WorkshopWaitHandle}" && exit 0 fi done --// - { SUB_USERDATA_URL: !FindInMap [DesignPatterns, options, UserDataURL], SUB_GUID: !FindInMap [AmazonLinuxAMI, !Ref "AWS::Region", GUID], SUB_REPL_ROLE: !GetAtt ['DDBReplicationRole', 'Arn'], } WorkshopSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security Group, kept for legacy reasons. VpcId: Fn::If: - CreateNewVPC - Ref: VPC - Ref: AWS::NoValue SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: Name Value: !Ref 'AWS::StackName' VPC: Condition: CreateNewVPC Type: AWS::EC2::VPC Properties: Tags: - Key: Name Value: DesignPatternsVPC CidrBlock: 10.10.11.0/24 EnableDnsSupport: true EnableDnsHostnames: true VPCSubnet: Condition: CreateNewVPC Type: AWS::EC2::Subnet Properties: MapPublicIpOnLaunch: true AvailabilityZone: Fn::Select: - '0' - Fn::GetAZs: !Ref AWS::Region VpcId: !Ref VPC CidrBlock: 10.10.11.0/24 VPCInternetGateway: Condition: CreateNewVPC Type: AWS::EC2::InternetGateway Properties: {} VPCAttachGateway: Condition: CreateNewVPC Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref 'VPCInternetGateway' VPCRouteTable: Condition: CreateNewVPC Type: AWS::EC2::RouteTable Properties: Tags: - Key: Name Value: DesignPatternsVPCRouteTable VpcId: !Ref VPC VPCSubnetRouteTableAssociation: Condition: CreateNewVPC Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref VPCSubnet RouteTableId: !Ref VPCRouteTable RouteVPCAny: Condition: CreateNewVPC Type: AWS::EC2::Route DependsOn: VPCAttachGateway Properties: RouteTableId: !Ref VPCRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref VPCInternetGateway Outputs: WorkshopEC2InstanceURL: Description: WorkshopEC2InstanceURL Value: !If [CreateNewVPC, !GetAtt 'WorkshopEC2InstanceCustomVPC.PublicDnsName', !GetAtt 'WorkshopEC2Instance.PublicDnsName']