internal LogSourceStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var logGroupName = new CfnParameter(this, "LogGroupName", new CfnParameterProps
            {
                Type        = "String",
                Description = "The name of the CloudWatch Log Group Name to apply the subscription."
            });

            if (!string.IsNullOrEmpty(logGroupName.ValueAsString))
            {
                Console.WriteLine("LogDestinationArn: ----------- " + logGroupName.ValueAsString);
            }
            _logGroupName = logGroupName.ValueAsString;

            var logDestinationArn = new CfnParameter(this, "LogDestinationArn", new CfnParameterProps
            {
                Type        = "String",
                Description = "The ARN of the LogDestination."
            });

            if (!string.IsNullOrEmpty(logDestinationArn.ValueAsString))
            {
                Console.WriteLine("LogGroupName: ----------- " + logDestinationArn.ValueAsString);
            }
            _logDestinationArn = logDestinationArn.ValueAsString;

            AddLogSubscription();
        }
예제 #2
0
        internal InfraStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var stackProps = props as InfraStackProps;

            var imageTag = new CfnParameter(this, "ImageTag", new CfnParameterProps
            {
                Type        = "String",
                Default     = "latest",
                Description = "The tag of the image that needs to be deployed to the lambda"
            });

            var dockerImageCode = DockerImageCode.FromEcr(Repository.FromRepositoryName(
                                                              this,
                                                              stackProps.EcrRepoName,
                                                              stackProps.EcrRepoName
                                                              ), new EcrImageCodeProps {
                Tag = imageTag.ValueAsString
            });

            var lambda = new DockerImageFunction(this, "TimesheetApi", new DockerImageFunctionProps
            {
                FunctionName = "TimesheetApi",
                Code         = dockerImageCode,
                Description  = "Timesheet API",
                Timeout      = Duration.Seconds(10),
            });

            // DynamoDb Table
            var table = new Table(this, "Timesheet", new TableProps
            {
                TableName    = "Timesheet",
                PartitionKey = new Attribute
                {
                    Name = "Id",
                    Type = AttributeType.STRING
                },
                SortKey = new Attribute
                {
                    Name = "Day",
                    Type = AttributeType.STRING
                }
            });

            // Assign permission to lambda to access the Timesheet Table
            lambda.AddToRolePolicy(new PolicyStatement(new PolicyStatementProps
            {
                Actions   = new[] { "dynamodb:*" },
                Resources = new[] { table.TableArn }
            }));
        }
        internal LogDestinationStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            this.DestinationAccountId = props.Env.Account;

            var sourceAcctNumParam = new CfnParameter(this, "SourceAccountNumber", new CfnParameterProps
            {
                Description = "Account Number which is given access to push the logs."
            });

            if (!string.IsNullOrEmpty(sourceAcctNumParam.ValueAsString))
            {
                Console.WriteLine("SourceAccountNumber: ----------- " + sourceAcctNumParam.ValueAsString);
            }
            this.SourceLogAccountId = sourceAcctNumParam.ValueAsString ?? props.Env.Account;

            CreateLogBucket();
            CreateLogConsumerResources();
        }
예제 #4
0
        public ApplicationStack(Construct scope, string id, HackatonStackProperties props) : base(scope, id, props)
        {
            var environmentName = new CfnParameter(this, "EnvironmentName", new CfnParameterProps
            {
                Type        = "String",
                Default     = "",
                Description = "The name of the environment to push the resources in"
            });


            Props = props;
            CreateS3Bucket(environmentName.ValueAsString);
            CreateEventsTable(environmentName.ValueAsString);
            CreateParticipantsTable(environmentName.ValueAsString);
            CreateBackersTable(environmentName.ValueAsString);

            CreateBackerNotifyParticipantFinishedQueue(environmentName.ValueAsString);
        }
예제 #5
0
        internal AutoScaledInstances(
            CdkStack stack,
            CfnParameter targetPlatform,
            Vpc vpc,
            bool publicAccess,
            SecurityGroup albSecurityGroup,
            SecurityGroup instanceSecurityGroup,
            Database database = null,
            Policy policy     = null,
            ApplicationLoadBalancer restApiLoadBalancer = null)
        {
            IMachineImage selectedImage;

            bool targetWindows = false;

            if (targetWindows)
            {
                var userData = UserData.ForWindows();
                userData.AddCommands(
                    "New-Item -Path c:\\temp -ItemType Directory -Force",
                    $"Read-S3Object -BucketName aws-codedeploy-{stack.Region}/latest -Key codedeploy-agent.msi -File c:\\temp\\codedeploy-agent.msi",
                    "Start-Process -Wait -FilePath c:\\temp\\codedeploy-agent.msi -WindowStyle Hidden"
                    );
                selectedImage = new WindowsImage(
                    WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_CORE_BASE,
                    new WindowsImageProps
                {
                    UserData = userData
                }
                    );
            }
            else
            {
                var userData = UserData.ForLinux(new LinuxUserDataOptions {
                    Shebang = "#!/bin/bash -xe"
                });
                userData.AddCommands(
                    "exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1",
                    "yum install -y aws-cli ruby jq",
                    "yum -y update",
                    "cd /tmp/",
                    $"curl -O https://aws-codedeploy-{stack.Region}.s3.amazonaws.com/latest/install",
                    "chmod +x ./install",
                    "if ./install auto; then",
                    "    echo \"CodeDeploy Agent installation completed successfully!\"",
                    "    exit 0",
                    "else",
                    "    echo \"CodeDeploy Agent installation failed, please investigate.\"",
                    "    rm -f /tmp/install",
                    "    exit 1",
                    "fi",
                    "rm -rf /tmp/*"
                    );
                selectedImage = new AmazonLinuxImage(new AmazonLinuxImageProps
                {
                    Edition        = AmazonLinuxEdition.STANDARD,
                    Virtualization = AmazonLinuxVirt.HVM,
                    Generation     = AmazonLinuxGeneration.AMAZON_LINUX_2,
                    Storage        = AmazonLinuxStorage.EBS,
                    UserData       = userData
                });
            };

            var alb = new ApplicationLoadBalancer(stack, $"ApplicationLoadBalancer-{(publicAccess ? "public" : "private")}", new ApplicationLoadBalancerProps
            {
                InternetFacing = publicAccess,
                Vpc            = vpc,
                VpcSubnets     = new SubnetSelection {
                    SubnetType = publicAccess ? SubnetType.PUBLIC : SubnetType.PRIVATE
                },
                SecurityGroup = albSecurityGroup,
                IpAddressType = IpAddressType.IPV4,
                Http2Enabled  = true
            });

            var albTargetGroup = new ApplicationTargetGroup(stack, $"ApplicationTargetGroup-{(publicAccess ? "public" : "private")}", new ApplicationTargetGroupProps
            {
                Vpc         = vpc,
                Port        = 80,
                Protocol    = ApplicationProtocol.HTTP,
                TargetType  = TargetType.INSTANCE,
                HealthCheck = new Amazon.CDK.AWS.ElasticLoadBalancingV2.HealthCheck
                {
                    Timeout  = Duration.Seconds(5),
                    Interval = Duration.Seconds(10),
                    HealthyThresholdCount = 2
                }
            });
            var albListener = new ApplicationListener(stack, $"ApplicationListener-{(publicAccess ? "public" : "private")}", new ApplicationListenerProps
            {
                Port          = 80,
                Protocol      = ApplicationProtocol.HTTP,
                DefaultAction = ListenerAction.Forward(new[] { albTargetGroup }),
                LoadBalancer  = alb
            });

            var asg = new AutoScalingGroup(stack, $"ASG-{(publicAccess ? "public" : "private")}", new AutoScalingGroupProps
            {
                Vpc          = vpc,
                MinCapacity  = 2,
                InstanceType = InstanceType.Of(InstanceClass.BURSTABLE3, InstanceSize.MEDIUM),
                MachineImage = selectedImage,
                BlockDevices = new[] {
                    new Amazon.CDK.AWS.AutoScaling.BlockDevice()
                    {
                        DeviceName = "/dev/xvda",
                        Volume     = Amazon.CDK.AWS.AutoScaling.BlockDeviceVolume.Ebs(
                            targetWindows ? 30: 8,
                            new Amazon.CDK.AWS.AutoScaling.EbsDeviceOptions {
                            VolumeType          = Amazon.CDK.AWS.AutoScaling.EbsDeviceVolumeType.GP2,
                            DeleteOnTermination = true
                        }
                            )
                    }
                },
                AssociatePublicIpAddress = false,
                VpcSubnets = new SubnetSelection {
                    SubnetType = SubnetType.PRIVATE
                }
            });

            if (policy != null)
            {
                asg.Role.AttachInlinePolicy(policy);
            }
            asg.Role.AddToPrincipalPolicy(
                new PolicyStatement(new PolicyStatementProps
            {
                Effect    = Effect.ALLOW,
                Actions   = new[] { "ec2:DescribeTags" },
                Resources = new[] { "*" }
            })
                );
            asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"));
            asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("AWSXRayDaemonWriteAccess"));
            asg.Role.AddManagedPolicy(ManagedPolicy.FromAwsManagedPolicyName("CloudWatchAgentServerPolicy"));

            Tag.Add(asg, "Application", stack.StackName);
            if (publicAccess)
            {
                Tag.Add(asg, "ApplicationRole", "Front End");
                Tag.Add(asg, "RESTAPIAddress", restApiLoadBalancer.LoadBalancerDnsName);
            }
            else
            {
                Tag.Add(asg, "ApplicationRole", "REST API");
            }
            if (database != null)
            {
                asg.Node.AddDependency(database.DatabaseResource);
                Tag.Add(asg, "DBSecretArn", database.Password.SecretArn);
            }

            // Enable access from the ALB
            asg.AddSecurityGroup(instanceSecurityGroup);
            Result = new LoadBalancedInstancesResult
            {
                AutoScalingGroup = asg,
                TargetGroup      = albTargetGroup,
                LoadBalancer     = alb
            };
        }
예제 #6
0
        internal CICD(
            CdkStack stack,
            CfnParameter targetPlatform,
            LoadBalancedInstancesResult frontEndInstanceInfo,
            LoadBalancedInstancesResult restAPIInstanceInfo)
        {
            var artifactBucket = new Bucket(stack, "ArtifactBucket");
            var repo           = new Repository(stack, "ApplicationRepository", new RepositoryProps
            {
                RepositoryName = stack.StackName,
                Description    = $"Contains the code for the {stack.StackName} application."
            });
            var cfnRepo = repo.Node.DefaultChild as Amazon.CDK.AWS.CodeCommit.CfnRepository;

            cfnRepo.Code = new CfnRepository.CodeProperty
            {
                S3 = new CfnRepository.S3Property
                {
                    Bucket = SourceBucketName,
                    Key    = SourceBucketKey
                }
            };

            var build = new PipelineProject(stack, "ApplicationBuild", new PipelineProjectProps
            {
                Environment = new BuildEnvironment
                {
                    BuildImage           = LinuxBuildImage.AMAZON_LINUX_2_3,
                    EnvironmentVariables = new Dictionary <string, IBuildEnvironmentVariable> {
                        {
                            "TARGET_OS",
                            new BuildEnvironmentVariable {
                                Type  = BuildEnvironmentVariableType.PLAINTEXT,
                                Value = targetPlatform.Value
                            }
                        }
                    }
                }
            });
            var frontEndApplication = new ServerApplication(stack, "FrontEnd", new ServerApplicationProps
            {
                ApplicationName = $"{stack.StackName}-FrontEnd"
            });
            var frontEndDeploymentGroup = new ServerDeploymentGroup(stack, "FrontEndDeploymentGroup", new ServerDeploymentGroupProps
            {
                Application         = frontEndApplication,
                DeploymentGroupName = $"{stack.StackName.ToLower()}-frontend-deployment-group",
                //Role = new Role(stack, "DeploymentServiceRole", new RoleProps
                //{
                //    AssumedBy = new ServicePrincipal("codedeploy.amazonaws.com"),
                //    Description = "Allows Application Deployment.",
                //    ManagedPolicies = new[] { ManagedPolicy.FromAwsManagedPolicyName("service-role/AWSCodeDeployRole") }
                //}),
                //AutoRollback = new AutoRollbackConfig { FailedDeployment = true },
                //DeploymentConfig = ServerDeploymentConfig.HALF_AT_A_TIME,
                LoadBalancer      = LoadBalancer.Application(frontEndInstanceInfo.TargetGroup),
                AutoScalingGroups = new[] { frontEndInstanceInfo.AutoScalingGroup }
            });
            var restApiApplication = new ServerApplication(stack, "RestAPI", new ServerApplicationProps
            {
                ApplicationName = $"{stack.StackName}-RESTAPI"
            });
            var restApiDeploymentGroup = new ServerDeploymentGroup(stack, "RestAPIDeploymentGroup", new ServerDeploymentGroupProps
            {
                Application         = restApiApplication,
                DeploymentGroupName = $"{stack.StackName.ToLower()}-restapi-deployment-group",
                //AutoRollback = new AutoRollbackConfig { FailedDeployment = true },
                //DeploymentConfig = ServerDeploymentConfig.HALF_AT_A_TIME,
                LoadBalancer      = LoadBalancer.Application(restAPIInstanceInfo.TargetGroup),
                AutoScalingGroups = new[] { restAPIInstanceInfo.AutoScalingGroup }
            });
            var sourceOutput      = new Artifact_();
            var frontEndArtifacts = new Artifact_("FrontEndOutput");
            var restAPIArtifacts  = new Artifact_("RESTAPIOutput");
            var pipeline          = new Pipeline(stack, "ApplicationPipeline", new PipelineProps
            {
                ArtifactBucket = artifactBucket,
                PipelineName   = $"{stack.StackName}-Pipeline",
                Stages         = new[]
                {
                    new Amazon.CDK.AWS.CodePipeline.StageProps
                    {
                        StageName = "Source",
                        Actions   = new []
                        {
                            new CodeCommitSourceAction(new CodeCommitSourceActionProps
                            {
                                ActionName = "Source",
                                Repository = repo,
                                Output     = sourceOutput
                            })
                        }
                    },
                    new Amazon.CDK.AWS.CodePipeline.StageProps
                    {
                        StageName = "Build",
                        Actions   = new []
                        {
                            new CodeBuildAction(new CodeBuildActionProps
                            {
                                ActionName = "CodeBuild",
                                Project    = build,
                                Input      = sourceOutput,
                                Outputs    = new [] { frontEndArtifacts, restAPIArtifacts }
                            })
                        }
                    },
                    new Amazon.CDK.AWS.CodePipeline.StageProps
                    {
                        StageName = "Deploy",
                        Actions   = new []
                        {
                            new CodeDeployServerDeployAction(new CodeDeployServerDeployActionProps {
                                ActionName      = "DeployFrontEnd",
                                DeploymentGroup = frontEndDeploymentGroup,
                                Input           = frontEndArtifacts
                            }),
                            new CodeDeployServerDeployAction(new CodeDeployServerDeployActionProps {
                                ActionName      = "DeployRESTAPI",
                                DeploymentGroup = restApiDeploymentGroup,
                                Input           = restAPIArtifacts
                            })
                        }
                    }
                }
            });
        }
예제 #7
0
        internal PipelineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var stackProps    = props as PipelineStackProps;
            var codestarArn   = $"arn:aws:codestar-connections:us-east-1:{this.Account}:connection/{stackProps.CodeStarConnectionId}";
            var encryptionKey = Key.FromKeyArn(this, "encryptionKey", $"arn:aws:kms:{this.Region}:{this.Account}:key/{stackProps.KMSKeyId}");

            var sourceArtifact = Bucket.FromBucketAttributes(this, "sourceArtifact", new BucketAttributes
            {
                BucketArn     = $"arn:aws:s3:::codepipeline-artifacts-{this.Account}-{this.Region}",
                EncryptionKey = encryptionKey
            });

            var pipelineRole = Role.FromRoleArn(this, "pipelineRole",
                                                $"arn:aws:iam::{this.Account}:role/CodePipelineMasterRole",
                                                new FromRoleArnOptions {
                Mutable = false
            }
                                                );

            var sourceOutputArtifact = new Artifact_();
            var cdkBuildOutput       = new Artifact_("CdkBuildOutput");

            var apiName = new CfnParameter(this, "ApiName", new CfnParameterProps
            {
                Type        = "String",
                Description = "The name of the API"
            });

            var apiNameLower = new CfnParameter(this, "ApiNameLower", new CfnParameterProps
            {
                Type        = "String",
                Description = "The name of the API in Lowercase"
            });

            //
            // Create the pipeline
            //
            var pipeline = new Pipeline(this, $"ApiPipeline", new PipelineProps
            {
                PipelineName   = $"{apiName.ValueAsString}ApiPipeline",
                ArtifactBucket = sourceArtifact,
                Role           = pipelineRole,
                Stages         = new[]
                {
                    new Amazon.CDK.AWS.CodePipeline.StageProps {
                        StageName = "Source",
                        Actions   = new []
                        {
                            new CodeStarConnectionsSourceAction(new CodeStarConnectionsSourceActionProps
                            {
                                ActionName         = "Github",
                                Branch             = "main",
                                Output             = sourceOutputArtifact,
                                Owner              = "kumaranbsundar",
                                Repo               = stackProps.RepoName,
                                TriggerOnPush      = true,
                                ConnectionArn      = codestarArn,
                                Role               = pipelineRole,
                                VariablesNamespace = "SourceVariables"
                            })
                        }
                    },
                    new Amazon.CDK.AWS.CodePipeline.StageProps {
                        StageName = "Build",
                        Actions   = new []
                        {
                            new CodeBuildAction(new CodeBuildActionProps {
                                ActionName           = "CDK_Synth",
                                Project              = GetCdkBuildProject(encryptionKey, pipelineRole),
                                Input                = sourceOutputArtifact,
                                Outputs              = new[] { cdkBuildOutput },
                                Role                 = pipelineRole,
                                EnvironmentVariables = new Dictionary <string, IBuildEnvironmentVariable> {
                                    {
                                        "API_NAME", new BuildEnvironmentVariable {
                                            Type  = BuildEnvironmentVariableType.PLAINTEXT,
                                            Value = apiNameLower.ValueAsString
                                        }
                                    }
                                }
                            })
                        }
                    }
                }
            });

            stackProps.DeployEnvs.ToList().ForEach(de =>
            {
                var crossAccountRole = Role.FromRoleArn(this, "crossAccountRole",
                                                        $"arn:aws:iam::{de.AccountId}:role/CodePipelineCrossAccountRole",
                                                        new FromRoleArnOptions {
                    Mutable = false
                }
                                                        );

                var deploymentRole = Role.FromRoleArn(this, "deploymentRole",
                                                      $"arn:aws:iam::{de.AccountId}:role/CodePipelineCfnDeploymentRole",
                                                      new FromRoleArnOptions {
                    Mutable = false
                }
                                                      );

                pipeline.AddStage(GetPipelineStage(
                                      de,
                                      apiNameLower.ValueAsString,
                                      cdkBuildOutput,
                                      crossAccountRole,
                                      deploymentRole,
                                      pipelineRole,
                                      sourceOutputArtifact,
                                      GetContainerBuildProject(encryptionKey, pipelineRole)
                                      ));
            });
        }
예제 #8
0
        internal CdkStack(Construct scope, string id, IStackProps props = null) :
            base(scope, id, props)
        {
            var targetPlatform = new CfnParameter(this, "TargetPlatform", new CfnParameterProps
            {
                AllowedValues = new[] { "Linux", "Windows" },
                Type          = "String",
                Default       = "Linux"
            });

            var vpc = new Vpc(this, "VPC", new VpcProps
            {
                Cidr               = "10.0.0.0/16",
                MaxAzs             = 2,
                NatGatewayProvider = NatProvider.Gateway(),
                NatGateways        = 2,
                NatGatewaySubnets  = new SubnetSelection {
                    SubnetType = SubnetType.PUBLIC, OnePerAz = true
                },
                SubnetConfiguration = new[]
                {
                    new SubnetConfiguration {
                        CidrMask = 24, Name = "PublicSubnet", SubnetType = SubnetType.PUBLIC
                    },
                    new SubnetConfiguration {
                        CidrMask = 24, Name = "PrivateSubnet", SubnetType = SubnetType.PRIVATE
                    },
                }
            });

            var externalLoadBalancerSecurityGroup = new SecurityGroup(vpc, "ExternalLoadBalancerSecurityGroup", new SecurityGroupProps
            {
                Vpc         = vpc,
                Description = "Allows HTTP access to the application."
            });

            externalLoadBalancerSecurityGroup.AddIngressRule(externalLoadBalancerSecurityGroup, Port.Tcp(80), "Allow HTTP");
            var frontEndSecurityGroup = new SecurityGroup(vpc, "UISecurityGroup", new SecurityGroupProps
            {
                Vpc         = vpc,
                Description = "Allows HTTP access to the UI."
            });

            frontEndSecurityGroup.AddIngressRule(externalLoadBalancerSecurityGroup, Port.Tcp(80), "Allow HTTP");
            var internalLoadBalancerSecurityGroup = new SecurityGroup(vpc, "InternalLoadBalancerSecurityGroup", new SecurityGroupProps
            {
                Vpc         = vpc,
                Description = "Allows HTTP access to the REST API."
            });

            internalLoadBalancerSecurityGroup.AddIngressRule(frontEndSecurityGroup, Port.Tcp(80));
            var restApiSecurityGroup = new SecurityGroup(vpc, "ApiSecurityGroup", new SecurityGroupProps
            {
                Vpc         = vpc,
                Description = "Allows HTTP access to the Rest API."
            });

            restApiSecurityGroup.AddIngressRule(internalLoadBalancerSecurityGroup, Port.Tcp(80));

            var db = new Database(this, vpc, restApiSecurityGroup);

            var policy = new Amazon.CDK.AWS.IAM.Policy(this,
                                                       "DBPasswordSecretAccess",
                                                       new Amazon.CDK.AWS.IAM.PolicyProps
            {
                PolicyName = "AllowPasswordAccess",
                Statements = new[] {
                    new Amazon.CDK.AWS.IAM.PolicyStatement(new Amazon.CDK.AWS.IAM.PolicyStatementProps {
                        Effect  = Amazon.CDK.AWS.IAM.Effect.ALLOW,
                        Actions = new [] {
                            "secretsmanager:GetSecretValue"
                        },
                        Resources = new [] { db.Password.SecretArn }
                    })
                }
            });
            var restApiInstances  = new AutoScaledInstances(this, targetPlatform, vpc, false, internalLoadBalancerSecurityGroup, restApiSecurityGroup, db, policy: policy);
            var frontEndInstances = new AutoScaledInstances(this, targetPlatform, vpc, true, externalLoadBalancerSecurityGroup, frontEndSecurityGroup, restApiLoadBalancer: restApiInstances.Result.LoadBalancer);

            //var instances = new AutoScaledInstances(this, targetPlatform, vpc, appSecurityGroup);
            new CICD(this, targetPlatform, frontEndInstances.Result, restApiInstances.Result);
        }
예제 #9
0
        public ApiStack(Construct parent, string id, IApiStackProps props) : base(parent, id, props)
        {
            var cluster = new Cluster(
                this,
                "Example",
                new ClusterProps
            {
                Vpc = props.Vpc,
            });

            var logging = new AwsLogDriver(new AwsLogDriverProps
            {
                StreamPrefix = "Example",
            });

            var taskDef = new FargateTaskDefinition(
                this,
                "Task",
                new FargateTaskDefinitionProps
            {
                MemoryLimitMiB = 512,
                Cpu            = 256,
            });

            var repo = Repository.FromRepositoryName(
                this,
                "EcrRepository",
                props.Repository.RepositoryName);

            var imageTag = new CfnParameter(
                this,
                props.ApiImageTag,
                new CfnParameterProps
            {
                Default = "latest",
            });

            var container = new ContainerDefinition(
                this,
                "ApiContainer",
                new ContainerDefinitionProps
            {
                TaskDefinition = taskDef,
                Image          = ContainerImage.FromEcrRepository(repo, imageTag.ValueAsString),
                Logging        = logging,
            });

            container.AddPortMappings(new PortMapping
            {
                ContainerPort = 80,
                HostPort      = 80,
                Protocol      = Amazon.CDK.AWS.ECS.Protocol.TCP,
            });

            var loadBalancer = new ApplicationLoadBalancer(
                this,
                "LoadBalancer",
                new ApplicationLoadBalancerProps
            {
                Vpc            = props.Vpc,
                Http2Enabled   = false,
                IdleTimeout    = Duration.Seconds(5),
                InternetFacing = true,
                IpAddressType  = IpAddressType.IPV4,
                VpcSubnets     = new SubnetSelection
                {
                    Subnets = props.Vpc.PublicSubnets,
                },
            });

            var ecsService = new ApplicationLoadBalancedFargateService(
                this,
                "Service",
                new ApplicationLoadBalancedFargateServiceProps
            {
                Cluster            = cluster,
                TaskDefinition     = taskDef,
                AssignPublicIp     = false,
                PublicLoadBalancer = true,
                LoadBalancer       = loadBalancer,
            });

            PrintLoadBalancerDnsName(ecsService);
        }