Beispiel #1
0
    def __init__(self, scope: cdk.Construct, id: str, ssh_key_name: str,
                 vpc: ec2.IVpc) -> None:

        super().__init__(scope=scope, id=id)

        # Role used for EC2 Instance Profile to connect to SSM
        self.iam_role = iam.Role(
            scope=self,
            id=f"{id}Ec2Role",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            description="Role assumed by wormhole ec2 instance",
        )

        # IAM policy for sending messages to SSM
        self.iam_policy = self._create_iam_policy(self, id)
        self.iam_policy.attach_to_role(self.iam_role)

        # Launch template that the AutoScaling Group will use to
        # spin up new/replacement instances.
        launch_template = ec2.LaunchTemplate(
            self,
            f'{id}LaunchTemplate',
            machine_image=ec2.MachineImage.latest_amazon_linux(),
            instance_type=ec2.InstanceType("t3a.micro"),
            key_name=ssh_key_name,
            role=self.iam_role,
            instance_initiated_shutdown_behavior=ec2.
            InstanceInitiatedShutdownBehavior.TERMINATE,
            security_group=ec2.SecurityGroup(self,
                                             f'{id}SG',
                                             vpc=vpc,
                                             allow_all_outbound=True))

        # The AutoScaling Group configuration.
        self.asg_ = asg.CfnAutoScalingGroup(
            self,
            f'{id}Asg',
            min_size="1",
            max_size="2",
            desired_capacity="1",
            launch_template=asg.CfnAutoScalingGroup.
            LaunchTemplateSpecificationProperty(
                version=launch_template.latest_version_number,
                launch_template_id=launch_template.launch_template_id,
                launch_template_name=launch_template.launch_template_name,
            ),
            # This tag will be used to find the running instance
            # that can be used for ssm wormhole.
            tags=[
                asg.CfnAutoScalingGroup.TagPropertyProperty(
                    key="ec2:purpose",
                    propagate_at_launch=True,
                    value="wormhole")
            ],
            vpc_zone_identifier=[
                subnet.subnet_id for subnet in vpc.private_subnets
            ],
        )
Beispiel #2
0
    def __init__(self, scope: core.Stack, id: str, **kwargs):
        super().__init__(scope, id, **kwargs)

        # This resource alone will create a private/public subnet in each AZ as well as nat/internet gateway(s)
        self.vpc = aws_ec2.Vpc(
            self,
            "BaseVPC",
            cidr='10.0.0.0/24',
        )

        # Creating ECS Cluster in the VPC created above
        self.ecs_cluster = aws_ecs.Cluster(self,
                                           "ECSCluster",
                                           vpc=self.vpc,
                                           cluster_name="container-demo")

        # Adding service discovery namespace to cluster
        self.ecs_cluster.add_default_cloud_map_namespace(name="service", )

        ###### EC2 SPOT CAPACITY PROVIDER SECTION ######

        ## As of today, AWS CDK doesn't support Launch Templates on the AutoScaling construct, hence it
        ## doesn't support Mixed Instances Policy to combine instance types on Auto Scaling and adhere to Spot best practices
        ## In the meantime, CfnLaunchTemplate and CfnAutoScalingGroup resources are used to configure Spot capacity
        ## https://github.com/aws/aws-cdk/issues/6734

        self.ecs_spot_instance_role = aws_iam.Role(
            self,
            "ECSSpotECSInstanceRole",
            assumed_by=aws_iam.ServicePrincipal("ec2.amazonaws.com"),
            managed_policies=[
                aws_iam.ManagedPolicy.from_aws_managed_policy_name(
                    "service-role/AmazonEC2ContainerServiceforEC2Role"),
                aws_iam.ManagedPolicy.from_aws_managed_policy_name(
                    "service-role/AmazonEC2RoleforSSM")
            ])

        self.ecs_spot_instance_profile = aws_iam.CfnInstanceProfile(
            self,
            "ECSSpotInstanceProfile",
            roles=[self.ecs_spot_instance_role.role_name])

        ## This creates a Launch Template for the Auto Scaling group
        self.lt = aws_ec2.CfnLaunchTemplate(
            self,
            "ECSEC2SpotCapacityLaunchTemplate",
            launch_template_data={
                "instanceType":
                "m5.large",
                "imageId":
                aws_ssm.StringParameter.value_for_string_parameter(
                    self,
                    "/aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id"
                ),
                "securityGroupIds": [
                    x.security_group_id
                    for x in self.ecs_cluster.connections.security_groups
                ],
                "iamInstanceProfile": {
                    "arn": self.ecs_spot_instance_profile.attr_arn
                },
                #
                ## Here we configure the ECS agent to drain Spot Instances upon catching a Spot Interruption notice from instance metadata
                "userData":
                core.Fn.base64(
                    core.Fn.sub(
                        "#!/usr/bin/bash\n"
                        "echo ECS_CLUSTER=${cluster_name} >> /etc/ecs/ecs.config\n"
                        "sudo iptables --insert FORWARD 1 --in-interface docker+ --destination 169.254.169.254/32 --jump DROP\n"
                        "sudo service iptables save\n"
                        "echo ECS_ENABLE_SPOT_INSTANCE_DRAINING=true >> /etc/ecs/ecs.config\n"
                        "echo ECS_AWSVPC_BLOCK_IMDS=true >> /etc/ecs/ecs.config\n"
                        "cat /etc/ecs/ecs.config",
                        variables={
                            "cluster_name": self.ecs_cluster.cluster_name
                        }))
            },
            launch_template_name="ECSEC2SpotCapacityLaunchTemplate")

        self.ecs_ec2_spot_mig_asg = aws_autoscaling.CfnAutoScalingGroup(
            self,
            "ECSEC2SpotCapacity",
            min_size="0",
            max_size="10",
            vpc_zone_identifier=[
                x.subnet_id for x in self.vpc.private_subnets
            ],
            mixed_instances_policy={
                "instancesDistribution": {
                    "onDemandAllocationStrategy": "prioritized",
                    "onDemandBaseCapacity": 0,
                    "onDemandPercentageAboveBaseCapacity": 0,
                    "spotAllocationStrategy": "capacity-optimized"
                },
                "launchTemplate": {
                    "launchTemplateSpecification": {
                        "launchTemplateId": self.lt.ref,
                        "version": self.lt.attr_default_version_number
                    },
                    "overrides": [{
                        "instanceType": "m5.large"
                    }, {
                        "instanceType": "m5d.large"
                    }, {
                        "instanceType": "m5a.large"
                    }, {
                        "instanceType": "m5ad.large"
                    }, {
                        "instanceType": "m5n.large"
                    }, {
                        "instanceType": "m5dn.large"
                    }, {
                        "instanceType": "m3.large"
                    }, {
                        "instanceType": "m4.large"
                    }, {
                        "instanceType": "t3.large"
                    }, {
                        "instanceType": "t2.large"
                    }]
                }
            })
        #
        core.Tag.add(self.ecs_ec2_spot_mig_asg, "Name",
                     self.ecs_ec2_spot_mig_asg.node.path)
        core.CfnOutput(self,
                       "EC2SpotAutoScalingGroupName",
                       value=self.ecs_ec2_spot_mig_asg.ref,
                       export_name="EC2SpotASGName")
        #
        ##### END EC2 SPOT CAPACITY PROVIDER SECTION #####

        # Namespace details as CFN output
        self.namespace_outputs = {
            'ARN':
            self.ecs_cluster.default_cloud_map_namespace.
            private_dns_namespace_arn,
            'NAME':
            self.ecs_cluster.default_cloud_map_namespace.
            private_dns_namespace_name,
            'ID':
            self.ecs_cluster.default_cloud_map_namespace.
            private_dns_namespace_id,
        }

        # Cluster Attributes
        self.cluster_outputs = {
            'NAME': self.ecs_cluster.cluster_name,
            'SECGRPS': str(self.ecs_cluster.connections.security_groups)
        }

        # When enabling EC2, we need the security groups "registered" to the cluster for imports in other service stacks
        if self.ecs_cluster.connections.security_groups:
            self.cluster_outputs['SECGRPS'] = str([
                x.security_group_id
                for x in self.ecs_cluster.connections.security_groups
            ][0])

        # Frontend service to backend services on 3000
        self.services_3000_sec_group = aws_ec2.SecurityGroup(
            self,
            "FrontendToBackendSecurityGroup",
            allow_all_outbound=True,
            description=
            "Security group for frontend service to talk to backend services",
            vpc=self.vpc)

        # Allow inbound 3000 from ALB to Frontend Service
        self.sec_grp_ingress_self_3000 = aws_ec2.CfnSecurityGroupIngress(
            self,
            "InboundSecGrp3000",
            ip_protocol='TCP',
            source_security_group_id=self.services_3000_sec_group.
            security_group_id,
            from_port=3000,
            to_port=3000,
            group_id=self.services_3000_sec_group.security_group_id)

        # Creating an EC2 bastion host to perform load test on private backend services
        amzn_linux = aws_ec2.MachineImage.latest_amazon_linux(
            generation=aws_ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
            edition=aws_ec2.AmazonLinuxEdition.STANDARD,
            virtualization=aws_ec2.AmazonLinuxVirt.HVM,
            storage=aws_ec2.AmazonLinuxStorage.GENERAL_PURPOSE)

        # Instance Role/profile that will be attached to the ec2 instance
        # Enabling service role so the EC2 service can use ssm
        role = aws_iam.Role(
            self,
            "InstanceSSM",
            assumed_by=aws_iam.ServicePrincipal("ec2.amazonaws.com"))

        # Attaching the SSM policy to the role so we can use SSM to ssh into the ec2 instance
        role.add_managed_policy(
            aws_iam.ManagedPolicy.from_aws_managed_policy_name(
                "service-role/AmazonEC2RoleforSSM"))

        # Reading user data, to install siege into the ec2 instance.
        with open("stresstool_user_data.sh") as f:
            user_data = f.read()

        # Instance creation
        self.instance = aws_ec2.Instance(
            self,
            "Instance",
            instance_name="{}-stresstool".format(stack_name),
            instance_type=aws_ec2.InstanceType("t3.medium"),
            machine_image=amzn_linux,
            vpc=self.vpc,
            role=role,
            user_data=aws_ec2.UserData.custom(user_data),
            security_group=self.services_3000_sec_group)

        # All Outputs required for other stacks to build
        core.CfnOutput(self,
                       "NSArn",
                       value=self.namespace_outputs['ARN'],
                       export_name="NSARN")
        core.CfnOutput(self,
                       "NSName",
                       value=self.namespace_outputs['NAME'],
                       export_name="NSNAME")
        core.CfnOutput(self,
                       "NSId",
                       value=self.namespace_outputs['ID'],
                       export_name="NSID")
        core.CfnOutput(self,
                       "FE2BESecGrp",
                       value=self.services_3000_sec_group.security_group_id,
                       export_name="SecGrpId")
        core.CfnOutput(self,
                       "ECSClusterName",
                       value=self.cluster_outputs['NAME'],
                       export_name="ECSClusterName")
        core.CfnOutput(self,
                       "ECSClusterSecGrp",
                       value=self.cluster_outputs['SECGRPS'],
                       export_name="ECSSecGrpList")
        core.CfnOutput(self,
                       "ServicesSecGrp",
                       value=self.services_3000_sec_group.security_group_id,
                       export_name="ServicesSecGrp")
        core.CfnOutput(self,
                       "StressToolEc2Id",
                       value=self.instance.instance_id)
        core.CfnOutput(self,
                       "StressToolEc2Ip",
                       value=self.instance.instance_private_ip)
Beispiel #3
0
    def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        current_directory = os.path.realpath(
            os.path.join(os.getcwd(), os.path.dirname(__file__)))
        allowed_values = yaml.load(open(
            os.path.join(current_directory, "..", "..",
                         "allowed_values.yaml")),
                                   Loader=yaml.SafeLoader)
        ami_mapping = {"AMI": {"OEJITSI": AMI_NAME}}
        for region in generated_ami_ids.keys():
            ami_mapping[region] = {"OEJITSI": generated_ami_ids[region]}
        aws_ami_region_map = core.CfnMapping(self,
                                             "AWSAMIRegionMap",
                                             mapping=ami_mapping)

        # utility function to parse the unique id from the stack id for
        # shorter resource names  using cloudformation functions
        def append_stack_uuid(name):
            return core.Fn.join("-", [
                name,
                core.Fn.select(
                    0,
                    core.Fn.split(
                        "-",
                        core.Fn.select(2, core.Fn.split(
                            "/", core.Aws.STACK_ID))))
            ])

        #
        # PARAMETERS
        #

        cidr_block_param = core.CfnParameter(
            self,
            "IngressCidrBlock",
            allowed_pattern="((\d{1,3})\.){3}\d{1,3}/\d{1,2}",
            default="0.0.0.0/0",
            description=
            "Required: A CIDR block to restrict access to the Jitsi application. Leave as 0.0.0.0/0 to allow public access from internet."
        )
        ec2_instance_type_param = core.CfnParameter(
            self,
            "InstanceType",
            allowed_values=allowed_values["allowed_instance_types"],
            default="t3.xlarge",
            description=
            "Required: The EC2 instance type for the application Auto Scaling Group."
        )
        jitsi_hostname_param = core.CfnParameter(
            self,
            "JitsiHostname",
            description=
            "Required: The hostname to access Jitsi. E.G. 'jitsi.internal.mycompany.com'"
        )
        jitsi_interface_app_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceAppName",
            default="Jitsi Meet",
            description=
            "Optional: Customize the app name on the Jitsi interface.")
        jitsi_interface_default_remote_display_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceDefaultRemoteDisplayName",
            default="Fellow Jitster",
            description=
            "Optional: Customize the default display name for Jitsi users.")
        jitsi_interface_native_app_name_param = core.CfnParameter(
            self,
            "JitsiInterfaceNativeAppName",
            default="Jitsi Meet",
            description=
            "Optional: Customize the native app name on the Jitsi interface.")
        jitsi_interface_show_brand_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceShowBrandWatermark",
            allowed_values=["true", "false"],
            default="true",
            description=
            "Optional: Display the watermark logo image in the upper left corner."
        )
        jitsi_interface_show_watermark_for_guests_param = core.CfnParameter(
            self,
            "JitsiInterfaceShowWatermarkForGuests",
            allowed_values=["true", "false"],
            default="true",
            description=
            "Optional: Display the watermark logo image in the upper left corner for guest users. This can be set to override the general setting behavior for guest users."
        )
        jitsi_interface_brand_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceBrandWatermark",
            default="",
            description=
            "Optional: Provide a URL to a PNG image to be used as the brand watermark logo image in the upper right corner. File should be publically available for download."
        )
        jitsi_interface_brand_watermark_link_param = core.CfnParameter(
            self,
            "JitsiInterfaceBrandWatermarkLink",
            default="http://jitsi.org",
            description=
            "Optional: Provide a link destination for the brand watermark logo image in the upper right corner."
        )
        jitsi_interface_watermark_param = core.CfnParameter(
            self,
            "JitsiInterfaceWatermark",
            default="",
            description=
            "Optional: Provide a URL to a PNG image to be used as the watermark logo image in the upper left corner. File should be publically available for download."
        )
        jitsi_interface_watermark_link_param = core.CfnParameter(
            self,
            "JitsiInterfaceWatermarkLink",
            default="http://jitsi.org",
            description=
            "Optional: Provide a link destination for the Jitsi watermark logo image in the upper left corner."
        )
        route_53_hosted_zone_name_param = core.CfnParameter(
            self,
            "Route53HostedZoneName",
            description=
            "Required: Route 53 Hosted Zone name in which a DNS record will be created by this template. Must already exist and be the domain part of the Jitsi Hostname parameter, without trailing dot. E.G. 'internal.mycompany.com'"
        )
        notification_email_param = core.CfnParameter(
            self,
            "NotificationEmail",
            default="",
            description=
            "Optional: Specify an email address to get emails about deploys, Let's Encrypt, and other system events."
        )

        #
        # CONDITIONS
        #

        notification_email_exists_condition = core.CfnCondition(
            self,
            "NotificationEmailExistsCondition",
            expression=core.Fn.condition_not(
                core.Fn.condition_equals(notification_email_param.value, "")))

        #
        # RESOURCES
        #

        # vpc
        vpc = Vpc(self, "Vpc")

        # sns
        sns_notification_topic = aws_sns.CfnTopic(
            self,
            "NotificationTopic",
            topic_name="{}-notifications".format(core.Aws.STACK_NAME))
        sns_notification_subscription = aws_sns.CfnSubscription(
            self,
            "NotificationSubscription",
            protocol="email",
            topic_arn=sns_notification_topic.ref,
            endpoint=notification_email_param.value_as_string)
        sns_notification_subscription.cfn_options.condition = notification_email_exists_condition
        iam_notification_publish_policy = aws_iam.PolicyDocument(statements=[
            aws_iam.PolicyStatement(effect=aws_iam.Effect.ALLOW,
                                    actions=["sns:Publish"],
                                    resources=[sns_notification_topic.ref])
        ])

        # cloudwatch
        app_log_group = aws_logs.CfnLogGroup(
            self, "JitsiAppLogGroup", retention_in_days=TWO_YEARS_IN_DAYS)
        app_log_group.cfn_options.update_replace_policy = core.CfnDeletionPolicy.RETAIN
        app_log_group.cfn_options.deletion_policy = core.CfnDeletionPolicy.RETAIN
        system_log_group = aws_logs.CfnLogGroup(
            self, "JitsiSystemLogGroup", retention_in_days=TWO_YEARS_IN_DAYS)
        system_log_group.cfn_options.update_replace_policy = core.CfnDeletionPolicy.RETAIN
        system_log_group.cfn_options.deletion_policy = core.CfnDeletionPolicy.RETAIN

        # iam
        iam_jitsi_instance_role = aws_iam.CfnRole(
            self,
            "JitsiInstanceRole",
            assume_role_policy_document=aws_iam.PolicyDocument(statements=[
                aws_iam.PolicyStatement(
                    effect=aws_iam.Effect.ALLOW,
                    actions=["sts:AssumeRole"],
                    principals=[aws_iam.ServicePrincipal("ec2.amazonaws.com")])
            ]),
            policies=[
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=[
                                "logs:CreateLogStream",
                                "logs:DescribeLogStreams", "logs:PutLogEvents"
                            ],
                            resources=[
                                app_log_group.attr_arn,
                                system_log_group.attr_arn
                            ])
                    ]),
                    policy_name="AllowStreamLogsToCloudWatch"),
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=[
                                "ec2:AssociateAddress", "ec2:DescribeVolumes",
                                "ec2:DescribeTags",
                                "cloudwatch:GetMetricStatistics",
                                "cloudwatch:ListMetrics",
                                "cloudwatch:PutMetricData"
                            ],
                            resources=["*"])
                    ]),
                    policy_name="AllowStreamMetricsToCloudWatch"),
                aws_iam.CfnRole.PolicyProperty(
                    policy_document=aws_iam.PolicyDocument(statements=[
                        aws_iam.PolicyStatement(
                            effect=aws_iam.Effect.ALLOW,
                            actions=["autoscaling:Describe*"],
                            resources=["*"])
                    ]),
                    policy_name="AllowDescribeAutoScaling"),
            ],
            managed_policy_arns=[
                "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
            ])

        # ec2
        jitsi_sg = aws_ec2.CfnSecurityGroup(
            self,
            "JitsiSg",
            group_description="Jitsi security group",
            vpc_id=vpc.id())

        eip = aws_ec2.CfnEIP(self, "Eip", domain="vpc")
        core.Tags.of(eip).add("Name", "{}/Eip".format(core.Aws.STACK_NAME))

        ec2_instance_profile = aws_iam.CfnInstanceProfile(
            self, "JitsiInstanceProfile", roles=[iam_jitsi_instance_role.ref])
        with open("jitsi/jitsi_launch_config_user_data.sh") as f:
            jitsi_launch_config_user_data = f.read()
        ec2_launch_config = aws_autoscaling.CfnLaunchConfiguration(
            self,
            "JitsiLaunchConfig",
            image_id=core.Fn.find_in_map("AWSAMIRegionMap", core.Aws.REGION,
                                         "OEJITSI"),
            instance_type=ec2_instance_type_param.value_as_string,
            iam_instance_profile=ec2_instance_profile.ref,
            security_groups=[jitsi_sg.ref],
            user_data=(core.Fn.base64(
                core.Fn.sub(
                    jitsi_launch_config_user_data, {
                        "JitsiHostname":
                        jitsi_hostname_param.value_as_string,
                        "JitsiPublicIP":
                        eip.ref,
                        "LetsEncryptCertificateEmail":
                        notification_email_param.value_as_string
                    }))))

        # autoscaling
        asg = aws_autoscaling.CfnAutoScalingGroup(
            self,
            "JitsiAsg",
            launch_configuration_name=ec2_launch_config.ref,
            desired_capacity="1",
            max_size="1",
            min_size="1",
            vpc_zone_identifier=vpc.public_subnet_ids())
        asg.cfn_options.creation_policy = core.CfnCreationPolicy(
            resource_signal=core.CfnResourceSignal(count=1, timeout="PT15M"))
        asg.cfn_options.update_policy = core.CfnUpdatePolicy(
            auto_scaling_rolling_update=core.CfnAutoScalingRollingUpdate(
                max_batch_size=1,
                min_instances_in_service=0,
                pause_time="PT15M",
                wait_on_resource_signals=True))
        core.Tags.of(asg).add("Name",
                              "{}/JitsiAsg".format(core.Aws.STACK_NAME))

        jitsi_http_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiHttpSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=80,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=80)
        jitsi_https_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiHttpsSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=443,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=443)
        jitsi_fallback_network_audio_video_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiFallbackNetworkAudioVideoSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=4443,
            group_id=jitsi_sg.ref,
            ip_protocol="tcp",
            to_port=4443)
        jitsi_general_network_audio_video_ingress = aws_ec2.CfnSecurityGroupIngress(
            self,
            "JitsiGeneralNetworkAudioVideoSgIngress",
            cidr_ip=cidr_block_param.value_as_string,
            from_port=10000,
            group_id=jitsi_sg.ref,
            ip_protocol="udp",
            to_port=10000)

        # route 53
        record_set = aws_route53.CfnRecordSet(
            self,
            "RecordSet",
            hosted_zone_name=
            f"{route_53_hosted_zone_name_param.value_as_string}.",
            name=jitsi_hostname_param.value_as_string,
            resource_records=[eip.ref],
            type="A")
        # https://github.com/aws/aws-cdk/issues/8431
        record_set.add_property_override("TTL", 60)

        # AWS::CloudFormation::Interface
        self.template_options.metadata = {
            "OE::Patterns::TemplateVersion": template_version,
            "AWS::CloudFormation::Interface": {
                "ParameterGroups": [{
                    "Label": {
                        "default": "Infrastructure Config"
                    },
                    "Parameters": [
                        jitsi_hostname_param.logical_id,
                        route_53_hosted_zone_name_param.logical_id,
                        cidr_block_param.logical_id,
                        ec2_instance_type_param.logical_id,
                        notification_email_param.logical_id
                    ]
                }, {
                    "Label": {
                        "default": "Jitsi Config"
                    },
                    "Parameters": [
                        jitsi_interface_app_name_param.logical_id,
                        jitsi_interface_default_remote_display_name_param.
                        logical_id,
                        jitsi_interface_native_app_name_param.logical_id,
                        jitsi_interface_show_brand_watermark_param.logical_id,
                        jitsi_interface_show_watermark_for_guests_param.
                        logical_id,
                        jitsi_interface_brand_watermark_param.logical_id,
                        jitsi_interface_brand_watermark_link_param.logical_id,
                        jitsi_interface_watermark_param.logical_id,
                        jitsi_interface_watermark_link_param.logical_id,
                    ]
                }, *vpc.metadata_parameter_group()],
                "ParameterLabels": {
                    cidr_block_param.logical_id: {
                        "default": "Ingress CIDR Block"
                    },
                    ec2_instance_type_param.logical_id: {
                        "default": "EC2 instance type"
                    },
                    jitsi_hostname_param.logical_id: {
                        "default": "Jitsi Hostname"
                    },
                    jitsi_interface_app_name_param.logical_id: {
                        "default": "Jitsi Interface App Name"
                    },
                    jitsi_interface_default_remote_display_name_param.logical_id:
                    {
                        "default":
                        "Jitsi Interface Default Remote Display Name"
                    },
                    jitsi_interface_native_app_name_param.logical_id: {
                        "default": "Jitsi Interface Native App Name"
                    },
                    jitsi_interface_show_brand_watermark_param.logical_id: {
                        "default": "Jitsi Interface Show Watermark"
                    },
                    jitsi_interface_show_watermark_for_guests_param.logical_id:
                    {
                        "default": "Jitsi Interface Show Watermark For Guests"
                    },
                    jitsi_interface_brand_watermark_param.logical_id: {
                        "default": "Jitsi Interface Watermark"
                    },
                    jitsi_interface_brand_watermark_link_param.logical_id: {
                        "default": "Jitsi Interface Watermark Link"
                    },
                    jitsi_interface_watermark_param.logical_id: {
                        "default": "Jitsi Interface Watermark"
                    },
                    jitsi_interface_watermark_link_param.logical_id: {
                        "default": "Jitsi Interface Watermark Link"
                    },
                    notification_email_param.logical_id: {
                        "default": "Notification Email"
                    },
                    route_53_hosted_zone_name_param.logical_id: {
                        "default": "AWS Route 53 Hosted Zone Name"
                    },
                    **vpc.metadata_parameter_labels()
                }
            }
        }

        #
        # OUTPUTS
        #

        eip_output = core.CfnOutput(
            self,
            "EipOutput",
            description=
            "The Elastic IP address dynamically mapped to the autoscaling group instance.",
            value=eip.ref)
        endpoint_output = core.CfnOutput(
            self,
            "JitsiUrl",
            description="The URL for the Jitsi instance.",
            value=core.Fn.join(
                "", ["https://", jitsi_hostname_param.value_as_string]))
Beispiel #4
0
    def __init__(self, app, id, target, **kwargs):
        """ Initializer """
        super().__init__(app, id, **kwargs)

        self.config = consolidate_context(self, target)

        # Fetch VPC info
        vpc = ec2.Vpc.from_lookup(self,
                                  "VPC",
                                  vpc_id=self.config["vpc_id"]["us-west-2"])
        subnets = vpc.private_subnets

        # AMI
        ami = ec2.MachineImage.generic_linux(
            {self.region: self.config["ami_name"]})

        # Create a specific SG for DT instances
        ec2_sg = ec2.SecurityGroup(self,
                                   "EC2SG",
                                   description='ec2 SG',
                                   vpc=vpc)
        # Add default rules for the SG
        self._add_default_rules(ec2_sg, vpc)

        # IAM role and profile for the EC2
        iam_role = iam.Role(
            self,
            "EC2Role",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            inline_policies={
                "extra-permissions":
                iam.PolicyDocument(statements=[
                    iam.PolicyStatement(
                        actions=["s3:*"],
                        resources=[
                            "arn:aws:iam::aws:policy/AmazonS3FullAccess"
                        ],
                    )
                ])
            },
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name="ReadOnlyAccess"),
                # Required by SSM StateManager
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    managed_policy_name="AmazonSSMManagedInstanceCore"),
            ],
        )
        iam_ip = iam.CfnInstanceProfile(
            self,
            "EC2InstProf",
            path="/",
            roles=[iam_role.role_name],
        )

        # Create a placement group in cluster mode to locate all the DT nodes close
        # to each other. This mode means we restrict ourself to a single AZ
        placement_group = ec2.CfnPlacementGroup(self,
                                                "EC2PG",
                                                strategy="cluster")

        instance_specs = self.config["instance_spec"]
        for key, spec in instance_specs.items():

            launch_template = ec2.CfnLaunchTemplate(
                self,
                f"EC2LT{key}",
                launch_template_data=ec2.CfnLaunchTemplate.
                LaunchTemplateDataProperty(
                    block_device_mappings=[
                        ec2.CfnLaunchTemplate.BlockDeviceMappingProperty(
                            device_name="/dev/xvda",
                            ebs=ec2.CfnLaunchTemplate.EbsProperty(
                                volume_size=self.config["ebs_volume_size"],
                                volume_type="gp2",
                            ),
                        )
                    ],
                    iam_instance_profile=ec2.CfnLaunchTemplate.
                    IamInstanceProfileProperty(arn=iam_ip.attr_arn),
                    image_id=str(ami.get_image(self).image_id),
                    instance_type=spec["instance_type"],
                    # TODO: we should use SSM Systems Manager rather than native SSH
                    key_name=self.config["key_pair"].format(
                        region=self.region),
                    security_group_ids=[ec2_sg.security_group_id]),
            )

            asg = autoscaling.CfnAutoScalingGroup(
                self,
                f"ASG{key}",
                desired_capacity="1",
                min_size="1",
                max_size="1",
                mixed_instances_policy=autoscaling.CfnAutoScalingGroup.
                MixedInstancesPolicyProperty(
                    instances_distribution=autoscaling.CfnAutoScalingGroup.
                    InstancesDistributionProperty(
                        on_demand_base_capacity=0,
                        on_demand_percentage_above_base_capacity=spec[
                            'on_demand_percentage_above_base_capacity'],
                        spot_allocation_strategy="lowest-price",
                        spot_instance_pools=1,
                    ),
                    launch_template=autoscaling.CfnAutoScalingGroup.
                    LaunchTemplateProperty(
                        launch_template_specification=autoscaling.
                        CfnAutoScalingGroup.
                        LaunchTemplateSpecificationProperty(
                            launch_template_id=launch_template.ref,
                            version=launch_template.attr_latest_version_number,
                        ))),
                # Use placement group, which means we restrict ourself to a single AZ
                placement_group=placement_group.ref,
                # Restrict to a single subnet because of the placement group
                vpc_zone_identifier=[subnets[0].subnet_id],
                # Set max instance lifetime to 7 days for worker nodes and 30 days for master
            )
            # Add a name to the ASG and it will be propagated to underlying EC2 instances
            tag_name = f'{self.config["prefix"]} ASG {key}'
            core.Tags.of(asg).add("Name", tag_name)