{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "Lab IDE using existing Subnet for container workshop v0.5", "Metadata": {}, "Parameters": { "SubnetId": { "Description": "Public Subnet for the Lab IDE.", "Type": "AWS::EC2::Subnet::Id", "Default": "" } }, "Mappings": {}, "Conditions": {}, "Resources": { "EksVpc": { "Type" : "AWS::CloudFormation::Stack", "Properties" : { "TemplateURL" : "https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/amazon-eks-vpc-sample.yaml" } }, "EksServiceRole": { "Type" : "AWS::CloudFormation::Stack", "Properties" : { "TemplateURL" : "https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-06-05/amazon-eks-service-role.yaml" } }, "LabIDE": { "Description": "-", "Type": "AWS::Cloud9::EnvironmentEC2", "Properties": { "Description": "Lab IDE for container workshop", "AutomaticStopTimeMinutes": 60, "InstanceType": "t2.micro", "Name": { "Ref": "AWS::StackName" }, "SubnetId": { "Ref": "SubnetId" } } }, "KopsStateStore": { "Description": "-", "Type": "AWS::S3::Bucket", "Properties": { "VersioningConfiguration": { "Status": "Enabled" } } }, "LabIdeRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ec2.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Policies": [ { "PolicyName": "eks-service", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "eks:*", "cloudformation:CreateStack", "cloudformation:UpdateStack" ], "Resource": "*" } ] } } ], "ManagedPolicyArns": [ "arn:aws:iam::aws:policy/AmazonEC2FullAccess", "arn:aws:iam::aws:policy/AmazonRoute53FullAccess", "arn:aws:iam::aws:policy/AmazonS3FullAccess", "arn:aws:iam::aws:policy/IAMFullAccess", "arn:aws:iam::aws:policy/AmazonVPCFullAccess", "arn:aws:iam::aws:policy/AWSCloudFormationReadOnlyAccess" ], "Path": "/" } }, "LabIdeInstanceProfile": { "Type": "AWS::IAM::InstanceProfile", "Properties": { "Path": "/", "Roles": [ { "Ref": "LabIdeRole" } ] } }, "AddRoleToInstance": { "Description": "Add LabIdeRole to Cloud9 IDE Instance", "Type": "Custom::AddRoleToInstance", "DependsOn": [ "AddRoleToInstanceFunction", "KopsStateStore" ], "Properties": { "ServiceToken": { "Fn::GetAtt": [ "AddRoleToInstanceFunction", "Arn" ] }, "Region": { "Ref": "AWS::Region" }, "StackName": { "Ref": "AWS::StackName" }, "EnvironmentId": { "Ref": "LabIDE" }, "LabIdeInstanceProfileName": { "Ref": "LabIdeInstanceProfile" }, "LabIdeInstanceProfileArn": { "Fn::GetAtt": [ "LabIdeInstanceProfile", "Arn" ] }, "BucketName": { "Ref": "KopsStateStore" } } }, "AddRoleToInstanceFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "ZipFile": { "Fn::Join": [ "\n", [ "from __future__ import print_function", "import boto3", "import logging", "import json", "import time", "import traceback", "import cfnresponse", "", "logger = logging.getLogger()", "logger.setLevel(logging.INFO)", "", "def handler(event, context):", " logger.debug('Event: {}'.format(event))", " logger.debug('Context: {}'.format(context))", " responseData = {}", " ", " # Immediately respond on Delete", " if event['RequestType'] == 'Delete':", " # Empty Bucket before CloudFormation deletes it", " session = boto3.Session()", " s3 = session.resource(service_name='s3')", " try:", " bucket = s3.Bucket(event['ResourceProperties']['BucketName'])", " bucket.object_versions.delete()", " ", " logger.info('Bucket '+event['ResourceProperties']['BucketName']+' objects/versions deleted.')", " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", " except Exception as e:", " logger.error(e, exc_info=True)", " 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 of the Cloud9 IDE", " instance = ec2.describe_instances(Filters=[{'Name': 'tag:Name','Values': ['aws-cloud9-'+event['ResourceProperties']['StackName']+'-'+event['ResourceProperties']['EnvironmentId']]}])['Reservations'][0]['Instances'][0]", " ", " # Create the IamInstanceProfile request object", " iam_instance_profile = {", " 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'],", " 'Name': event['ResourceProperties']['LabIdeInstanceProfileName']", " }", " ", " # 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']])", " ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId'])", " ", " responseData = {'Success': 'Role added to instance'+instance['InstanceId']+'.'}", " cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID')", " except Exception as e:", " logger.error(e, exc_info=True)", " responseData = {'Error': traceback.format_exc(e)}", " cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID')" ] ] } }, "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "LambdaExecutionRole", "Arn" ] }, "Runtime": "python2.7", "Timeout": "30" } }, "LambdaExecutionRole": { "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": [ "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:ReplaceIamInstanceProfileAssociation", "iam:ListInstanceProfiles", "iam:PassRole" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "s3:*" ], "Resource": [ { "Fn::GetAtt": [ "KopsStateStore", "Arn" ] }, { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "KopsStateStore", "Arn" ] }, "/*" ] ] } ] } ] } } ] } } }, "Outputs": { "Cloud9IDE": { "Value": { "Fn::Join": [ "", [ "https://", { "Ref": "AWS::Region" }, ".console.aws.amazon.com/cloud9/ide/", { "Ref": "LabIDE" }, "?region=", { "Ref": "AWS::Region" } ] ] } }, "EksServiceRoleArn": { "Value": { "Fn::GetAtt": ["EksServiceRole", "Outputs.RoleArn"] } }, "EksVpcId": { "Value": { "Fn::GetAtt": ["EksVpc", "Outputs.VpcId"] } }, "EksVpcSubnetIds": { "Value": { "Fn::GetAtt": ["EksVpc", "Outputs.SubnetIds"] } }, "EksVpcSecurityGroups": { "Value": { "Fn::GetAtt": ["EksVpc", "Outputs.SecurityGroups"] } } } }