Description: > Cloud9 Environment for One Observability Workshop Parameters: EnvironmentName: Description: An environment name that is prefixed to resource names Type: String Default: "observabilityworkshop" CreateVPC: Description: Create a VPC for Cloud9 instance Type: String Default: True AllowedValues: - True - False Cloud9VPC: Description: If VPC is not created by the template, must provide the VPC Id to deploy the instance Type: String Default: AWS::NoValue Cloud9Subnet: Description: If VPC is not created by the template, must provide the subnet ID to deploy the instance. Type: String Default: AWS::NoValue VpcCIDR: Description: Please enter the IP range (CIDR notation) for this VPC Type: String Default: 10.10.0.0/16 PublicSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone Type: String Default: 10.10.10.0/24 PublicSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone Type: String Default: 10.10.20.0/24 PrivateSubnet1CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone Type: String Default: 10.10.30.0/24 PrivateSubnet2CIDR: Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone Type: String Default: 10.10.40.0/24 C9InstanceType: Description: Example Cloud9 instance type Type: String Default: t3.medium AllowedValues: - t3.small - t3.medium - m5.large ConstraintDescription: Must be a valid Cloud9 instance type C9InstanceVolumeSize: Type: Number Description: The Size in GB of the Cloud9 Instance Volume. Default: 30 C9Image: Type: String Description: The Image to use for the Cloud9Instance Default: ubuntu-22.04-x86_64 PatchC9Instance: Description: Creates an Association to patch the Cloud9 Instance upon launch. Type: String Default: False AllowedValues: - True - False Cloud9OwnerRole: Description: Associate a specific owner role for the C9 Instance. Cloudformation will append 'arn:_partition_:iam::_account_id_:' to this value. Type: String Default: AWS::NoValue Conditions: CreatePatchAssociation: !Equals - !Ref PatchC9Instance - True CreateVPC: !Equals - !Ref CreateVPC - True SkipOwnerArn: !Equals - !Ref Cloud9OwnerRole - AWS::NoValue Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "General" Parameters: - EnvironmentName - Label: default: "Network Configuration" Parameters: - CreateVPC - VpcCIDR - PublicSubnet1CIDR - PublicSubnet2CIDR - PrivateSubnet1CIDR - PrivateSubnet2CIDR - Label: default: "Cloud9 Configuration" Parameters: - Cloud9VPC - Cloud9Subnet - C9InstanceType - C9InstanceVolumeSize - C9Image - PatchC9Instance - Cloud9OwnerRole Resources: VPC: Type: AWS::EC2::VPC Condition: CreateVPC Metadata: cfn_nag: rules_to_suppress: - id: W60 reason: "No need for Flow logs in this workshop" cdk_nag: rules_to_suppress: - id: AwsSolutions-VPC7 reason: "No need for Flow logs in this workshop" Properties: CidrBlock: !Ref VpcCIDR EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Ref EnvironmentName InternetGateway: Type: AWS::EC2::InternetGateway Condition: CreateVPC Properties: Tags: - Key: Name Value: !Ref EnvironmentName InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Condition: CreateVPC Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC PublicSubnet1: Type: AWS::EC2::Subnet Condition: CreateVPC Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PublicSubnet1CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Subnet (AZ1) PublicSubnet2: Type: AWS::EC2::Subnet Condition: CreateVPC Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PublicSubnet2CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Subnet (AZ2) PrivateSubnet1: Type: AWS::EC2::Subnet Condition: CreateVPC Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 0, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet1CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Subnet (AZ1) PrivateSubnet2: Type: AWS::EC2::Subnet Condition: CreateVPC Properties: VpcId: !Ref VPC AvailabilityZone: !Select [ 1, !GetAZs '' ] CidrBlock: !Ref PrivateSubnet2CIDR MapPublicIpOnLaunch: false Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Subnet (AZ2) NatGateway1EIP: Type: AWS::EC2::EIP Condition: CreateVPC DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway2EIP: Type: AWS::EC2::EIP Condition: CreateVPC DependsOn: InternetGatewayAttachment Properties: Domain: vpc NatGateway1: Type: AWS::EC2::NatGateway Condition: CreateVPC Properties: AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 NatGateway2: Type: AWS::EC2::NatGateway Condition: CreateVPC Properties: AllocationId: !GetAtt NatGateway2EIP.AllocationId SubnetId: !Ref PublicSubnet2 PublicRouteTable: Type: AWS::EC2::RouteTable Condition: CreateVPC Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName} Public Routes DefaultPublicRoute: Type: AWS::EC2::Route Condition: CreateVPC DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateVPC Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet1 PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateVPC Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet2 PrivateRouteTable1: Type: AWS::EC2::RouteTable Condition: CreateVPC Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Routes (AZ1) DefaultPrivateRoute1: Type: AWS::EC2::Route Condition: CreateVPC Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateVPC Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 PrivateRouteTable2: Type: AWS::EC2::RouteTable Condition: CreateVPC Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub ${EnvironmentName} Private Routes (AZ2) DefaultPrivateRoute2: Type: AWS::EC2::Route Condition: CreateVPC Properties: RouteTableId: !Ref PrivateRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateVPC Properties: RouteTableId: !Ref PrivateRouteTable2 SubnetId: !Ref PrivateSubnet2 NoIngressSecurityGroup: Type: AWS::EC2::SecurityGroup Metadata: cfn_nag: rules_to_suppress: - id: W5 reason: "Outbound access to the world is needed to download dependencies in C9" Properties: GroupDescription: "Security group with no ingress rule" VpcId: !If [CreateVPC, !Ref VPC, !Ref Cloud9VPC ] SecurityGroupEgress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Description: "HTTP Outbound traffic" - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 Description: "HTTPS Outbound traffic" ################## PERMISSIONS AND ROLES ################# C9Role: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W43 reason: "C9 Instance will be used to deploy Infrastructure in the workshop" - id: W28 reason: "Needed in order to check the proper role is attached to the instance before running CDK" - id: W11 reason: "Describe calls doesn't support resource filter. Volume tags are not propagated by Cloud9" cdk_nag: rules_to_suppress: - id: AwsSolutions-IAM5 reason: "Describe calls doesn't support resource filter. Volume tags are not propagated by Cloud9" Properties: RoleName: !Sub "${EnvironmentName}-admin" Tags: - Key: Environment Value: !Sub ${EnvironmentName} AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com - ssm.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess #Pending least privilege update Path: "/" Policies: - PolicyName: Fn::Join: - '' - - C9InstanceDenyPolicy- - Ref: AWS::Region PolicyDocument: Version: '2012-10-17' Statement: - Effect: Deny Action: - cloud9:UpdateEnvironment Resource: "*" - PolicyName: Fn::Join: - '' - - C9ResizeVolumeRead- - Ref: AWS::Region PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ec2:DescribeVolumesModifications - ec2:DescribeInstances Resource: "*" - PolicyName: Fn::Join: - '' - - C9ResizeVolumeWrite- - Ref: AWS::Region PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ec2:ModifyVolume Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:volume/*" C9LambdaExecutionRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "Describe Action doesn't support any resource condition" cdk_nag: rules_to_suppress: - id: AwsSolutions-IAM5 reason: "Lambda role must be able to describe and update the Instance profile associated with the C9 environment" Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: # - "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole" - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" Policies: - PolicyName: Fn::Join: - '' - - C9LambdaPolicy- - Ref: AWS::Region PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - cloudformation:DescribeStacks - cloudformation:DescribeStackEvents - cloudformation:DescribeStackResource - cloudformation:DescribeStackResources Resource: !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}" - Effect: Allow Action: - ec2:AssociateIamInstanceProfile - ec2:ModifyInstanceAttribute - ec2:ReplaceIamInstanceProfileAssociation Resource: !Sub "arn:${AWS::Partition}:ec2:${AWS::Region}:${AWS::AccountId}:instance/*" - Effect: Allow Action: - ec2:DescribeInstances - ec2:DescribeIamInstanceProfileAssociations Resource: "*" - Effect: Allow Action: - iam:ListInstanceProfiles Resource: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:instance-profile/* - Effect: Allow Action: - iam:PassRole Resource: Fn::GetAtt: - C9Role - Arn ################## LAMBDA BOOTSTRAP FUNCTION ################ C9BootstrapInstanceLambda: Type: Custom::C9BootstrapInstanceLambda DependsOn: - C9LambdaExecutionRole Properties: Tags: - Key: Environment Value: !Sub ${EnvironmentName} ServiceToken: Fn::GetAtt: - C9BootstrapInstanceLambdaFunction - Arn REGION: Ref: AWS::Region StackName: Ref: AWS::StackName EnvironmentId: Ref: C9Instance LabIdeInstanceProfileName: Ref: C9InstanceProfile LabIdeInstanceProfileArn: Fn::GetAtt: - C9InstanceProfile - Arn # LambdaSecurityGroup: # Type: AWS::EC2::SecurityGroup # Properties: # GroupDescription: "HTTPS Outbound Traffic" # VpcId: !Ref VPC # SecurityGroupEgress: # - IpProtocol: tcp # FromPort: 443 # ToPort: 443 # CidrIp: 0.0.0.0/0 # Description: "HTTPS Outbound traffic" C9BootstrapInstanceLambdaFunction: Type: AWS::Lambda::Function #checkov:skip=CKV_AWS_116:DLQ not needed for a single invocation Lambda #checkov:skip=CKV_AWS_117:Lambda only talks with AWS endpoints, no need for a VPC Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: "VPC support not needed for this helper lambda" Properties: Tags: - Key: Environment Value: AWS Example Handler: index.lambda_handler Role: Fn::GetAtt: - C9LambdaExecutionRole - Arn Runtime: python3.11 # VpcConfig: # SecurityGroupIds: # - !Ref LambdaSecurityGroup # SubnetIds: # - !Ref PrivateSubnet1 # - !Ref PrivateSubnet2 MemorySize: 256 Timeout: 600 ReservedConcurrentExecutions: 1 Code: ZipFile: !Sub | from __future__ import print_function import boto3 import json import os import time import traceback import cfnresponse import logging logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) def lambda_handler(event, context): logger.info('event: {}'.format(event)) logger.info('context: {}'.format(context)) responseData = {} 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-${EnvironmentName}'+'-'+event['ResourceProperties']['EnvironmentId']]}])['Reservations'][0]['Instances'][0] logger.info('instance: {}'.format(instance)) # Create the IamInstanceProfile request object iam_instance_profile = { 'Arn': event['ResourceProperties']['LabIdeInstanceProfileArn'], 'Name': event['ResourceProperties']['LabIdeInstanceProfileName'] } logger.info('iam_instance_profile: {}'.format(iam_instance_profile)) # Wait for Instance to become ready before adding Role instance_state = instance['State']['Name'] logger.info('instance_state: {}'.format(instance_state)) while instance_state != 'running': time.sleep(5) instance_state = ec2.describe_instances(InstanceIds=[instance['InstanceId']]) logger.info('instance_state: {}'.format(instance_state)) # attach instance profile response = ec2.associate_iam_instance_profile(IamInstanceProfile=iam_instance_profile, InstanceId=instance['InstanceId']) logger.info('response - associate_iam_instance_profile: {}'.format(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: logger.error(e, exc_info=True) # responseData = {'Error': traceback.format_exc(e)} responseData = {'Error':'There was a problem associating IAM profile to the Cloud9 Instance'} cfnresponse.send(event, context, cfnresponse.FAILED, responseData, 'CustomResourcePhysicalID') else: responseData = {'Success': 'Update or delete event'} cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, 'CustomResourcePhysicalID') ################## SSM BOOTSTRAP HANDLER ############### C9OutputBucket: #checkov:skip=CKV_AWS_18:S3 Access Logs not needed for the Association result Type: AWS::S3::Bucket Metadata: cfn_nag: rules_to_suppress: - id: W35 reason: "Access Logs aren't needed for this bucket" cdk_nag: rules_to_suppress: - id: AwsSolutions-S1 reason: "Bucket used to store Systems Manager Association results for troubleshooting, no need for access logs" DeletionPolicy: Retain UpdateReplacePolicy: Retain Properties: VersioningConfiguration: Status: Enabled LifecycleConfiguration: Rules: - Id: Expire1Day ExpirationInDays: 1 NoncurrentVersionExpirationInDays: 1 Status: Enabled AccessControl: Private BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true C9OutputBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref C9OutputBucket PolicyDocument: Version: 2012-10-17 Statement: - Action: - 's3:GetObject' - 's3:PutObject' - 's3:PutObjectAcl' Effect: Allow Resource: !Sub 'arn:${AWS::Partition}:s3:::${C9OutputBucket}/*' Principal: AWS: !GetAtt C9Role.Arn Condition: Bool: 'aws:SecureTransport': true - Action: 's3:*' Principal: '*' Effect: Deny Resource: - !Sub 'arn:${AWS::Partition}:s3:::${C9OutputBucket}/*' - !Sub 'arn:${AWS::Partition}:s3:::${C9OutputBucket}' Condition: Bool: 'aws:SecureTransport': false BootstrapWaitHandle: Type: AWS::CloudFormation::WaitConditionHandle BootstrapWaitCondition: Type: AWS::CloudFormation::WaitCondition Properties: Handle: !Ref BootstrapWaitHandle Timeout: "3600" C9PatchDocument: Condition: CreatePatchAssociation Type: AWS::SSM::Document Properties: Tags: - Key: Environment Value: !Sub ${EnvironmentName} DocumentType: Command Content: schemaVersion: '2.2' description: Bootstrap Cloud9 Instance mainSteps: - action: aws:runDocument name: PatchInstance inputs: documentPath: AWS-RunPatchBaseline documentType: SSMDocument documentParameters: Operation: Install C9SSMDocument: Type: AWS::SSM::Document Properties: Tags: - Key: Environment Value: !Sub ${EnvironmentName} DocumentType: Command Content: schemaVersion: '2.2' description: Bootstrap Cloud9 Instance mainSteps: - action: aws:runShellScript name: ResizeVolume inputs: runCommand: - !Sub SIZE=${C9InstanceVolumeSize} - !Sub REGION=${AWS::Region} - | INSTANCEID=$(curl http://169.254.169.254/latest/meta-data/instance-id) VOLUMEID=$(aws ec2 describe-instances \ --instance-id $INSTANCEID \ --query "Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId" \ --output text --region $REGION) aws ec2 modify-volume --volume-id $VOLUMEID --size $SIZE --region $REGION while [ \ "$(aws ec2 describe-volumes-modifications \ --volume-id $VOLUMEID \ --filters Name=modification-state,Values="optimizing","completed" \ --query "length(VolumesModifications)"\ --output text --region $REGION)" != "1" ]; do sleep 1 done sudo growpart /dev/nvme0n1 1 sudo resize2fs /dev/nvme0n1p1 - action: aws:runShellScript name: CreateSwap inputs: runCommand: - sudo dd if=/dev/zero of=/swapfile bs=128M count=32 - sudo chmod 600 /swapfile - sudo mkswap /swapfile - sudo swapon /swapfile - sudo /bin/bash -c 'echo "/swapfile swap swap defaults 0 0" >> /etc/fstab' - action: aws:runShellScript name: DependencyInstall inputs: runCommand: - apt update -y - apt install -y jq gettext bash-completion moreutils - pip install --user --upgrade awscli - sudo -i -u ubuntu bash -l -c "nvm install node --default" - sudo -i -u ubuntu bash -l -c "nvm alias default node" - sudo -i -u ubuntu bash -l -c "nvm exec node npm install -g aws-cdk --force" - action: aws:runShellScript name: KubectlInstall inputs: runCommand: - curl -o kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.28.5/2024-01-04/bin/linux/amd64/kubectl - chmod +x kubectl && sudo mv kubectl /usr/local/bin/ - echo "source <(kubectl completion bash)" >> /home/ubuntu/.bashrc - action: aws:runShellScript name: HelmInstall inputs: runCommand: - curl --location "https://get.helm.sh/helm-v3.13.0-linux-amd64.tar.gz" | tar xz -C /tmp - sudo mv /tmp/linux-amd64/helm /usr/local/bin/helm - action: aws:runShellScript name: eksctlInstall inputs: runCommand: - curl --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp - sudo mv /tmp/eksctl /usr/local/bin - action: aws:runShellScript name: cloneRepository inputs: runCommand: - su - ubuntu -c 'mkdir -p ~/environment/workshopfiles && git clone https://github.com/aws-samples/one-observability-demo.git ~/environment/workshopfiles/one-observability-demo' - action: aws:runShellScript name: NotifyCloudformation inputs: finallyStep: true runCommand: - | python --version && touch /home/ubuntu/environment/READY - !Sub | /usr/bin/python -c " import json import os import urllib3 import uuid signal_url='${BootstrapWaitHandle}' result = 'FAILURE' if os.path.exists('/home/ubuntu/environment/READY'): result = 'SUCCESS' encoded_body = json.dumps({ 'Status': result, 'Reason': 'Association completed', 'UniqueId': str(uuid.uuid4()), 'Data': 'Association completed' }) http = urllib3.PoolManager() http.request('PUT', signal_url , body=encoded_body) " - | [ -f /home/ubuntu/environment/READY ] && exit 0 || exit 2 C9BootstrapAssociation: Type: AWS::SSM::Association Properties: Name: !Ref C9SSMDocument AssociationName: !Sub Cloud9Bootstrap-${EnvironmentName} OutputLocation: S3Location: OutputS3BucketName: !Ref C9OutputBucket OutputS3KeyPrefix: bootstrapoutput Targets: - Key: tag:SSMBootstrap Values: - !Ref EnvironmentName C9PatchAssociation: Type: AWS::SSM::Association Condition: CreatePatchAssociation Properties: Name: !Ref C9PatchDocument AssociationName: !Sub Cloud9Patch-${EnvironmentName} OutputLocation: S3Location: OutputS3BucketName: !Ref C9OutputBucket OutputS3KeyPrefix: bootstrapoutput Targets: - Key: tag:SSMBootstrap Values: - !Ref EnvironmentName ################## INSTANCE ##################### # AWSCloud9SSMAccessRole: # Type: AWS::IAM::Role # Properties: # AssumeRolePolicyDocument: # Version: 2012-10-17 # Statement: # - Effect: Allow # Principal: # Service: # - cloud9.amazonaws.com # - ec2.amazonaws.com # Action: # - 'sts:AssumeRole' # Description: 'Service linked role for AWS Cloud9' # Path: '/service-role/' # ManagedPolicyArns: # - arn:${AWS::Partition}:iam::aws:policy/AWSCloud9SSMInstanceProfile # RoleName: 'AWSCloud9SSMAccessRole' # AWSCloud9SSMInstanceProfile: # Type: "AWS::IAM::InstanceProfile" # Properties: # InstanceProfileName: AWSCloud9SSMInstanceProfile # Path: "/cloud9/" # Roles: # - # Ref: AWSCloud9SSMAccessRole C9InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - Ref: C9Role C9Instance: DependsOn: C9BootstrapAssociation Type: AWS::Cloud9::EnvironmentEC2 Properties: Description: !Sub AWS Cloud9 instance for ${EnvironmentName} SubnetId: !If [CreateVPC, !Ref PublicSubnet1, !Ref Cloud9Subnet ] AutomaticStopTimeMinutes: 3600 ConnectionType: CONNECT_SSH ImageId: !Ref C9Image InstanceType: Ref: C9InstanceType Name: !Ref EnvironmentName OwnerArn: !If [SkipOwnerArn, !Ref "AWS::NoValue", !Sub "arn:${AWS::Partition}:iam::${AWS::AccountId}:${Cloud9OwnerRole}"] Tags: - Key: SSMBootstrap Value: !Ref EnvironmentName - Key: Environment Value: !Ref EnvironmentName Outputs: VPC: Description: A reference to the created VPC Value: !If [ CreateVPC , !Ref VPC, AWS::NoValue ] PublicSubnets: Description: A list of the public subnets Value: !If [ CreateVPC, !Join [ ",", [ !Ref PublicSubnet1, !Ref PublicSubnet2 ]], AWS::NoValue] PrivateSubnets: Description: A list of the private subnets Value: !If [ CreateVPC, !Join [ ",", [ !Ref PrivateSubnet1, !Ref PrivateSubnet2 ]], AWS::NoValue] PublicSubnet1: Description: A reference to the public subnet in the 1st Availability Zone Value: !If [ CreateVPC, !Ref PublicSubnet1, AWS::NoValue ] PublicSubnet2: Description: A reference to the public subnet in the 2nd Availability Zone Value: !If [ CreateVPC, !Ref PublicSubnet2, AWS:NoValue] PrivateSubnet1: Description: A reference to the private subnet in the 1st Availability Zone Value: !If [ CreateVPC, !Ref PrivateSubnet1, AWS:NoValue] PrivateSubnet2: Description: A reference to the private subnet in the 2nd Availability Zone Value: !If [ CreateVPC, !Ref PrivateSubnet2, AWS::NoValue] Cloud9RoleArn: Description: Role used by the Cloud9 Instance Value: !Ref C9Role Export: Name: !Sub "${AWS::StackName}-Cloud9Role" NoIngressSecurityGroup: Description: Security group with no ingress rule Value: !Ref NoIngressSecurityGroup Cloud9IDE: Value: Fn::Join: - '' - - https:// - Ref: AWS::Region - ".console.aws.amazon.com/cloud9/ide/" - Ref: C9Instance - "?region=" - Ref: AWS::Region