{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "Lab IDE using new public subnet for container workshop v0.5", "Metadata": {}, "Parameters": {}, "Mappings": { "SubnetConfig": { "VPC": { "CIDR": "10.0.0.0/16" }, "Public": { "CIDR": "10.0.0.0/24" } } }, "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" } }, "VPC": { "Type": "AWS::EC2::VPC", "Properties": { "EnableDnsSupport": "true", "EnableDnsHostnames": "true", "CidrBlock": { "Fn::FindInMap": [ "SubnetConfig", "VPC", "CIDR" ] }, "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackName" } }, { "Key": "Network", "Value": "Public" } ] } }, "PublicSubnet": { "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": { "Ref": "VPC" }, "CidrBlock": { "Fn::FindInMap": [ "SubnetConfig", "Public", "CIDR" ] }, "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackName" } }, { "Key": "Network", "Value": "Public" } ] } }, "InternetGateway": { "Type": "AWS::EC2::InternetGateway", "Properties": { "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackName" } }, { "Key": "Network", "Value": "Public" } ] } }, "GatewayToInternet": { "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "VpcId": { "Ref": "VPC" }, "InternetGatewayId": { "Ref": "InternetGateway" } } }, "PublicRouteTable": { "Type": "AWS::EC2::RouteTable", "Properties": { "VpcId": { "Ref": "VPC" }, "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackName" } }, { "Key": "Network", "Value": "Public" } ] } }, "PublicRoute": { "Type": "AWS::EC2::Route", "DependsOn": "GatewayToInternet", "Properties": { "RouteTableId": { "Ref": "PublicRouteTable" }, "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": { "Ref": "InternetGateway" } } }, "PublicSubnetRouteTableAssociation": { "Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": { "SubnetId": { "Ref": "PublicSubnet" }, "RouteTableId": { "Ref": "PublicRouteTable" } } }, "PublicNetworkAcl": { "Type": "AWS::EC2::NetworkAcl", "Properties": { "VpcId": { "Ref": "VPC" }, "Tags": [ { "Key": "Application", "Value": { "Ref": "AWS::StackName" } }, { "Key": "Network", "Value": "Public" } ] } }, "InboundSSHPublicNetworkAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "PublicNetworkAcl" }, "RuleNumber": "101", "Protocol": "6", "RuleAction": "allow", "Egress": "false", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "22", "To": "22" } } }, "InboundEphemeralPublicNetworkAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "PublicNetworkAcl" }, "RuleNumber": "103", "Protocol": "6", "RuleAction": "allow", "Egress": "false", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "1024", "To": "65535" } } }, "OutboundPublicNetworkAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "PublicNetworkAcl" }, "RuleNumber": "100", "Protocol": "6", "RuleAction": "allow", "Egress": "true", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "0", "To": "65535" } } }, "PublicSubnetNetworkAclAssociation": { "Type": "AWS::EC2::SubnetNetworkAclAssociation", "Properties": { "SubnetId": { "Ref": "PublicSubnet" }, "NetworkAclId": { "Ref": "PublicNetworkAcl" } } }, "LabIDE": { "Description": "-", "Type": "AWS::Cloud9::EnvironmentEC2", "Properties": { "Description": "Lab IDE for container workshop", "AutomaticStopTimeMinutes": 60, "InstanceType": "t2.micro", "Name": { "Ref": "AWS::StackName" }, "SubnetId": { "Ref": "PublicSubnet" } } }, "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"] } } } }