예제 #1
0
 // Asserts that the specified HostedZone is valid and represents the same
 // HostedZone that we initially created at the very start of this test.
 private void assertValidCreatedHostedZone(HostedZone hostedZone)
 {
     Assert.AreEqual(CALLER_REFERENCE, hostedZone.CallerReference);
     Assert.AreEqual(ZONE_NAME, hostedZone.Name);
     Assert.IsNotNull(hostedZone.Id);
     Assert.AreEqual(COMMENT, hostedZone.Config.Comment);
 }
예제 #2
0
        public FrontendStack(Construct scope, string name, string url, StackProps props = null) : base(scope, $"frontend-{name}", props)
        {
            // pricing - hosted zone
            // 0,50 USD por zona hospedada/mês para as primeiras 25 zonas hospedadas
            // 0,10 USD por zona hospedada/mês para zonas hospedadas adicionais
            // Consultas padrão
            // 0,40 USD por milhão de consultas – primeiro 1 bilhão de consultas/mês
            // 0,20 USD por milhão de consultas – mais de 1 bilhão de consultas/mês
            // Consultas de roteamento baseado em latência
            // 0,60 USD por milhão de consultas – primeiro 1 bilhão de consultas/mês
            // 0,30 USD por milhão de consultas – mais de 1 bilhão de consultas/mês
            // Consultas de Geo DNS e geoproximidade
            // 0,70 USD por milhão de consultas -- primeiro 1 bilhão de consultas/mês
            // 0,35 USD por milhão de consultas -- mais de 1 bilhão de consultas/mês

            _bucket = new Bucket(this, $"frontend-{name}-bucket", new BucketProps()
            {
                BucketName           = name + "72b302bf297a228a75730123efef7c41",
                WebsiteIndexDocument = "index.html",
                PublicReadAccess     = true,
                RemovalPolicy        = RemovalPolicy.DESTROY
            });

            _bucketDeployment = new BucketDeployment(this, $"frontend-{name}-deployment", new BucketDeploymentProps()
            {
                Sources           = new[] { Source.Asset("../tools/frontend/") },
                DestinationBucket = _bucket,
                RetainOnDelete    = false
            });

            _hostedZone = new HostedZone(this, $"frontend-{name}-hostedzone", new HostedZoneProps
            {
                ZoneName = url
            });

            _certificate = new Certificate(this, $"frontend-{name}-certificate", new CertificateProps
            {
                DomainName = url,
                Validation = CertificateValidation.FromDns(_hostedZone)
            });

            _distribution = new Distribution(this, $"frontend-{name}-distribution", new DistributionProps
            {
                DefaultBehavior = new BehaviorOptions
                {
                    Origin = new S3Origin(_bucket),
                    ViewerProtocolPolicy = ViewerProtocolPolicy.REDIRECT_TO_HTTPS
                },
                DomainNames       = new[] { url },
                Certificate       = _certificate,
                DefaultRootObject = "index.html"
            });

            _aRecord = new ARecord(this, $"frontend-{name}-arecord", new ARecordProps
            {
                Zone       = _hostedZone,
                RecordName = url,
                Target     = RecordTarget.FromAlias(new CloudFrontTarget(_distribution))
            });
        }
예제 #3
0
        public IEnumerable <ResourceRecordSet> GetResourceRecordSets(HostedZone zone)
        {
            var request = new ListResourceRecordSetsRequest(zone.Id);
            ListResourceRecordSetsResponse response = _route53Client.ListResourceRecordSets(request);

            return(response.ResourceRecordSets);
        }
예제 #4
0
        /// <summary>
        /// Retrieve the hosted zone for a specific domain.
        /// </summary>
        /// <param name="domain">The name of the domain</param>
        /// <param name="settings">The <see cref="Route53Settings"/> required to connect to Route53.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
        public async Task <HostedZone> GetHostedZone(string domain, Route53Settings settings, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (String.IsNullOrEmpty(domain))
            {
                throw new ArgumentNullException("domain");
            }



            IList <HostedZone> zones = await this.GetHostedZones(settings);

            HostedZone zone = null;

            if (zones != null)
            {
                zone = zones.FirstOrDefault(z => z.Name == domain);
            }

            if (zone == null)
            {
                _Log.Error("Could not find a hosted zone with the domain {0}", domain);
            }

            return(zone);
        }
예제 #5
0
        private static List <ResourceRecord> GetResourceRecords(IAwsClient awsClient, IEnumerable <StackResource> stackResources, AwsStack awsStack, string hostedZoneName)
        {
            var resourceRecords = new List <ResourceRecord>();

            if (stackResources.Any(x => x.ResourceType == ResourceTypeConstants.RecordSet))
            {
                string     zoneName   = hostedZoneName.Trim(new[] { '.' });
                HostedZone hostedZone = awsClient.DnsService.GetHostedZoneByName(zoneName);

                IEnumerable <ResourceRecordSet> resourceRecordSets = awsClient.DnsService.GetResourceRecordSets(hostedZone);
                string nameFilter = string.Format("{0}.{1}", awsStack.StackName, zoneName);

                resourceRecords = resourceRecordSets
                                  .Where(x => x.Name.ToLower().Contains(nameFilter.ToLower()))
                                  .Select(x => new ResourceRecord
                {
                    FullyQualifiedDomainName = x.Name,
                    TimeToLive = (int)x.TTL,
                    Type       = x.Type,
                    Values     = x.ResourceRecords.Select(rr => rr.Value).ToList(),
                    ResourceId = x.Name                             // resource records don't have an ID in AWS, but this is unique so it's good enough for now
                }).ToList();
            }

            return(resourceRecords);
        }
예제 #6
0
        private bool ApplyDnsChange(HostedZone zone, ResourceRecordSet recordSet, ChangeAction action)
        {
            // Prepare change as Batch
            Change changeDetails = new Change()
            {
                ResourceRecordSet = recordSet,
                Action            = action
            };

            ChangeBatch changeBatch = new ChangeBatch()
            {
                Changes = new List <Change> {
                    changeDetails
                }
            };

            // Prepare zone's resource record sets
            var recordsetRequest = new ChangeResourceRecordSetsRequest()
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = changeBatch
            };

            logger.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSets: {recordsetRequest.ChangeBatch} ");

            var recordsetResponse = route53Client.ChangeResourceRecordSets(recordsetRequest);

            logger.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSets Response: {recordsetResponse} ");

            logger.Info("DNS change completed.");

            return(true);
        }
예제 #7
0
        public void CreateHostedZoneByName()
        {
            Template   template       = StackTest.GetNewBlankTemplateWithVpc($"Vpc{this.TestContext.TestName}");
            string     hostedZoneName = $"{this.TestContext.TestName}{Guid.NewGuid().ToString().Replace("-", string.Empty)}.com";
            HostedZone hz             = new HostedZone(hostedZoneName);

            template.Resources.Add(hostedZoneName, hz);
            StackTest.CreateTestStack(template, this.TestContext);
        }
예제 #8
0
 private ARecord AddRoute53Alias(string hostedZoneUrl, string applicationUrl, DomainName domainName)
 {
     return(new ARecord(stack, "Route53Alias", new ARecordProps {
         Zone = HostedZone.FromLookup(stack, "HostedZone", new HostedZoneProviderProps {
             DomainName = hostedZoneUrl
         }),
         RecordName = applicationUrl,
         Target = RecordTarget.FromAlias(new ApiGatewayv2Domain(domainName))
     }));
 }
예제 #9
0
        public void CreatePrivateHostedZone()
        {
            Template   template       = StackTest.GetNewBlankTemplateWithVpc($"VpcCreatePrivateHostedZone");
            string     hostedZoneName = $"{this.TestContext.TestName}{Guid.NewGuid().ToString().Replace("-", string.Empty)}";
            HostedZone hz             = new HostedZone("example.com");

            template.Resources.Add(hostedZoneName, hz);
            hz.AddVpc(template.Vpcs.First(), Region.UsEast1);
            StackTest.CreateTestStack(template, this.TestContext);
        }
예제 #10
0
        internal Stack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            //setup the image
            var asset = new DockerImageAsset(this, $"{Config.AppName}Image", new DockerImageAssetProps {
                Directory = Path.Combine(System.Environment.CurrentDirectory, "api"),
            });

            //Create the Fargate service
            var vpc = Vpc.FromLookup(
                this, "sandbox", new VpcLookupOptions
            {
                VpcName = "sandbox_vpc"
            }
                );

            var cluster = new Cluster(this, $"{Config.AppName}Cluster", new ClusterProps
            {
                Vpc = vpc
            });

            var applicationDomain = $"{Config.ApplicationSubdomain}.{Config.DomainName}";
            var hostedZone        = HostedZone.FromLookup(
                this, "HostedZone", new HostedZoneProviderProps
            {
                DomainName  = $"{Config.DomainName}.",
                PrivateZone = false
            }
                );

            // Create a load-balanced Fargate service and make it public
            var fargateService = new ApplicationLoadBalancedFargateService(this, $"{Config.AppName}Service",
                                                                           new ApplicationLoadBalancedFargateServiceProps
            {
                Cluster          = cluster,     // Required
                DesiredCount     = 1,           // Default is 1
                TaskImageOptions = new ApplicationLoadBalancedTaskImageOptions
                {
                    Image = ContainerImage.FromDockerImageAsset(asset)
                },
                MemoryLimitMiB     = 1024,      // Default is 256
                PublicLoadBalancer = true,      // Default is false
                DomainName         = applicationDomain,
                DomainZone         = hostedZone,
            }
                                                                           );

            new CfnOutput(
                this, "Route53Url", new CfnOutputProps
            {
                Value       = applicationDomain,
                Description = "Nice Route53 Url"
            }
                );
        }
예제 #11
0
        private async Task UpdateRoute53(HostedZone zone, Instance instance, ILambdaContext context)
        {
            if (zone == null)
            {
                return;
            }

            string hostName = instance.Tags.GetTag("HostName");

            if (string.IsNullOrWhiteSpace(hostName))
            {
                context.Logger.LogLine($"Hostname missing!");
                return;
            }

            string ipAddress = instance.PublicIpAddress;

            if (string.IsNullOrWhiteSpace(ipAddress))
            {
                context.Logger.LogLine($"ipAddress missing!");
                return;
            }

            context.Logger.LogLine($"Update Zone: {zone.Name} host: {hostName} with IpAddress: {ipAddress}");

            ChangeResourceRecordSetsRequest request = new ChangeResourceRecordSetsRequest
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = new ChangeBatch
                {
                    Changes = new List <Change>
                    {
                        new Change
                        {
                            Action            = ChangeAction.UPSERT,
                            ResourceRecordSet = new ResourceRecordSet
                            {
                                Name            = $"{hostName}.{zone.Name}",
                                Type            = "A",
                                ResourceRecords = new List <ResourceRecord>
                                {
                                    new ResourceRecord {
                                        Value = ipAddress
                                    }
                                },
                                TTL = 60,
                            }
                        }
                    }
                }
            };
            await _amazonRoute53Client.ChangeResourceRecordSetsAsync(request);
        }
예제 #12
0
        public async Task <string> GetHostedZoneIdByName(string hostedZoneName)
        {
            using (AmazonRoute53Client route53Client = GetAmazonRoute53Client())
            {
                ListHostedZonesByNameResponse zones = await route53Client.ListHostedZonesByNameAsync(new ListHostedZonesByNameRequest()
                {
                    DNSName = hostedZoneName
                });

                HostedZone matchingZone = zones?.HostedZones.FirstOrDefault(zone => zone.Name == hostedZoneName);
                return(matchingZone?.Id);
            }
        }
        public void Route53GetHostedZone()
        {
            #region to-get-information-about-a-hosted-zone-1481752361124

            var response = client.GetHostedZone(new GetHostedZoneRequest
            {
                Id = "Z3M3LMPEXAMPLE"
            });

            DelegationSet delegationSet = response.DelegationSet;
            HostedZone    hostedZone    = response.HostedZone;

            #endregion
        }
예제 #14
0
        private async Task UpdateRoute53Entry(EC2InstanceStateChangeEvent cloudWatchEvent,
                                              ILambdaContext context)
        {
            Instance instance = await GetInstanceDetails(cloudWatchEvent.Detail.InstanceId, context);

            if (instance != null)
            {
                HostedZone zone = await GetZone(instance, context);

                await UpdateRoute53(zone, instance, context);
            }
            else
            {
                context.Logger.LogLine($"**** InstanceId Not Found: {cloudWatchEvent.Detail.InstanceId}");
            }
        }
예제 #15
0
        /// <summary>
        /// Instance is stopping
        ///
        /// Remove the route 53 entry for it.
        /// </summary>
        /// <param name="cloudWatchEvent"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task RemoveRoute53Entry(EC2InstanceStateChangeEvent cloudWatchEvent,
                                              ILambdaContext context)
        {
            Instance instance = await GetInstanceDetails(cloudWatchEvent.Detail.InstanceId, context);

            if (instance != null)
            {
                HostedZone zone = await GetZone(instance, context);

                if (zone == null)
                {
                    return;
                }

                await DeleteRoute53RecordSet(zone, instance, context);
            }
        }
예제 #16
0
        private async Task <bool> ApplyDnsChange(HostedZone zone, ResourceRecordSet recordSet, ChangeAction action)
        {
            // prepare change
            var changeDetails = new Change()
            {
                ResourceRecordSet = recordSet,
                Action            = action
            };

            var changeBatch = new ChangeBatch()
            {
                Changes = new List <Change> {
                    changeDetails
                }
            };

            // Update the zone's resource record sets
            var recordsetRequest = new ChangeResourceRecordSetsRequest()
            {
                HostedZoneId = zone.Id,
                ChangeBatch  = changeBatch
            };

            _log?.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSetsAsync: {JsonConvert.SerializeObject(recordsetRequest.ChangeBatch)} ");

            var recordsetResponse = await _route53Client.ChangeResourceRecordSetsAsync(recordsetRequest);

            _log?.Debug($"Route53 :: ApplyDnsChange : ChangeResourceRecordSetsAsync Response: {JsonConvert.SerializeObject(recordsetResponse)} ");

            // Monitor the change status
            var changeRequest = new GetChangeRequest()
            {
                Id = recordsetResponse.ChangeInfo.Id
            };

            while (ChangeStatus.PENDING == (await _route53Client.GetChangeAsync(changeRequest)).ChangeInfo.Status)
            {
                System.Diagnostics.Debug.WriteLine("DNS change is pending.");
                await Task.Delay(1500);
            }

            _log?.Information("DNS change completed.");

            return(true);
        }
예제 #17
0
        private async static Task <string> GetHostedZoneIdByName(string hostedZoneName)
        {
            AmazonRoute53Config config = new AmazonRoute53Config()
            {
                RegionEndpoint = RegionEndpoint.GetBySystemName(Settings.AWSRegion) // TODO: inject
            };

            using (AmazonRoute53Client route53Client = new AmazonRoute53Client(Settings.AWSAccessKeyId, Settings.AWSAccessKeySecret, config))
            {
                ListHostedZonesByNameResponse zones = await route53Client.ListHostedZonesByNameAsync(new ListHostedZonesByNameRequest()
                {
                    DNSName = hostedZoneName
                });

                HostedZone matchingZone = zones?.HostedZones.FirstOrDefault(zone => zone.Name == hostedZoneName);
                return(matchingZone?.Id);
            }
        }
예제 #18
0
        private static NetworkConfig GetNetworkConfig(Stack stack)
        {
            var result = new NetworkConfig();

            result.Zone = HostedZone.FromLookup(stack, "Zone", new HostedZoneProviderProps {
                DomainName = baseDomainName
            });

            result.Certificate = Certificate.FromCertificateArn(stack, "Certificate", "arn:aws:acm:us-east-1:249221827206:certificate/72c985a5-1576-4348-954b-34e4161d05f5");

            //result.Certificate = new DnsValidatedCertificate(stack, "SiteCertificate", new DnsValidatedCertificateProps
            //{
            //    DomainName = $"*.{baseDomainName}",
            //    HostedZone = result.Zone,
            //    Region = "us-east-1" // Cloudfront only checks this region for certificates.
            //});

            return(result);
        }
예제 #19
0
        public void copyNsRecordsFromSubDomainToParentDomain(string fullSubDomainName, string parentName)
        {
            Task <ListHostedZonesResponse> ListOfHostedZones = listAwsHostedZonesAsync();

            ListOfHostedZones.Wait();

            HostedZone subZone    = ListOfHostedZones.Result.HostedZones.Find(h => h.Name == $"{ fullSubDomainName }.");
            HostedZone parentZone = ListOfHostedZones.Result.HostedZones.Find(h => h.Name == $"{ parentName }.");

            Task <ListResourceRecordSetsResponse> getResourceRecords = getAwsResourceRecordsAsync(subZone.Id);

            getResourceRecords.Wait();
            ResourceRecordSet rrs = getResourceRecords.Result.ResourceRecordSets.Find(r => r.Name == $"{ fullSubDomainName }." && r.Type == RRType.NS);


            Task <ChangeResourceRecordSetsResponse> ResourceResponse = createAws_NS_ResourceRecords(parentZone.Id, fullSubDomainName, rrs);

            ResourceResponse.Wait();
        }
        private async Task <IEnumerable <ResourceRecordSet> > FindRecordSetsAsync(HostedZone zone, string dnsName, string recordType)
        {
            _logger.LogDebug("Finding record sets for {RecordType} {DnsName} in zone {Zone}", recordType, dnsName, zone.Name);

            var result        = new List <ResourceRecordSet>();
            var rootedDnsName = dnsName.EndsWith(DomainSegmentSeparator.ToString()) ? dnsName : dnsName + DomainSegmentSeparator;
            var remainder     = dnsName.Replace(zone.Name, String.Empty);

            var recordSets = await _route53Client.ListResourceRecordSetsAsync(
                new ListResourceRecordSetsRequest()
            {
                HostedZoneId    = zone.Id,
                StartRecordType = RRType.FindValue(recordType),
                StartRecordName = dnsName
            });

            do
            {
                foreach (var recordSet in recordSets.ResourceRecordSets)
                {
                    if (recordSet.Name.ToLower().Equals(rootedDnsName.ToLower()))
                    {
                        result.Add(recordSet);
                    }
                }

                recordSets = await _route53Client.ListResourceRecordSetsAsync(
                    new ListResourceRecordSetsRequest()
                {
                    HostedZoneId          = zone.Id,
                    StartRecordType       = recordSets.NextRecordType,
                    StartRecordName       = recordSets.NextRecordName,
                    StartRecordIdentifier = recordSets.NextRecordIdentifier
                });
            } while (recordSets.IsTruncated);

            _logger.LogInformation("{Count} record sets were found for {RecordType} {DnsName} in zone {Zone}", result.Count, recordType, dnsName, zone.Name);

            return(result);
        }
        public void RecordSetByNewHostZoneTest()
        {
            Template   template = StackTest.GetNewBlankTemplateWithVpc($"Vpc{this.TestContext.TestName}");
            HostedZone hz       = new HostedZone("zeta.yadayada.software.");

            template.Resources.Add("hostedZoneRecordSetByNewHostZoneTest", hz);
            hz.AddVpc(template.Vpcs.First(), Region.UsEast1);
            var target = RecordSet.AddByHostedZone(template, "test", hz, "test.zeta.yadayada.software.", RecordSet.RecordSetTypeEnum.A);

            target.TTL           = "60";
            target.RecordSetType = RecordSet.RecordSetTypeEnum.A.ToString();
            var DMZSubnet = new Subnet(template.Vpcs.First(), "10.0.0.0/20", AvailabilityZone.UsEast1A, true);

            template.Resources.Add("DMZSubnet", DMZSubnet);

            Instance testBox = new Instance(DMZSubnet, InstanceTypes.T2Micro, "ami-60b6c60a", OperatingSystem.Linux);

            template.Resources.Add("testbox", testBox);
            var eip = testBox.AddElasticIp();

            target.AddResourceRecord(eip);
            StackTest.CreateTestStack(template, this.TestContext);
        }
예제 #22
0
        public static async Task Create(this HostedZone zone, Credentials credentials)
        {
            var client = credentials.CreateClient();

            //[3] Create a resource record set change batch
            var recordSet = new ResourceRecordSet {
                Name            = zone.Name,
                TTL             = 60,
                Type            = RRType.A,
                ResourceRecords = new List <ResourceRecord> {
                    new ResourceRecord {
                        Value = "192.0.2.235"
                    }
                }
            };

            var change1 = new Change {
                ResourceRecordSet = recordSet,
                Action            = ChangeAction.CREATE
            };

            var changeBatch = new ChangeBatch {
                Changes = new List <Change> {
                    change1
                }
            };

            //[4] Update the zone's resource record sets
            var recordsetRequest = new ChangeResourceRecordSetsRequest {
                HostedZoneId = zone.Id,
                ChangeBatch  = changeBatch
            };

            var recordsetResponse = client.ChangeResourceRecordSets(recordsetRequest);

            var status = await recordsetResponse.ChangeInfo.WaitForStatusAync(credentials);
        }
예제 #23
0
        public ApiStack(Construct scope, string id, ApiProps props = null) : base(scope, id, props)
        {
            var hostedZone = HostedZone.FromHostedZoneAttributes(this, "HostedZone", new HostedZoneAttributes
            {
                ZoneName     = props.HostedZoneName,
                HostedZoneId = props.HostedZoneId
            });

            FargateService = new ApplicationLoadBalancedFargateService(this, $"{props.ServiceName}-fargate-service", new ApplicationLoadBalancedFargateServiceProps
            {
                ServiceName      = props.ServiceName,
                Cluster          = props.EcsCluster,
                TaskImageOptions = new ApplicationLoadBalancedTaskImageOptions
                {
                    ContainerName = props.ServiceName,
                    Image         = ContainerImage.FromEcrRepository(props.EcrRepository),
                    Environment   = props.ContainerEnvVars,
                    Secrets       = props.ContainerSecrets,
                    EnableLogging = true
                },
                Certificate = props.Certificate,
                DomainName  = $"{props.SubDomain}.{props.HostedZoneName}",
                DomainZone  = hostedZone,
                //this has an internet-facing ALB open to the world - could enhance security by hiding behind an API gateway
            });

            FargateService.TargetGroup.ConfigureHealthCheck(new HealthCheck
            {
                Path = "/health"
            });

            ApiUrl = $"https://{props.SubDomain}.{props.HostedZoneName}";

            //seems handy https://github.com/aws/aws-cdk/issues/8352
            //also handy https://chekkan.com/iam-policy-perm-for-public-load-balanced-ecs-fargate-on-cdk/
        }
예제 #24
0
 // Asserts that the specified HostedZone is valid and represents the same
 // HostedZone that we initially created at the very start of this test.
 private void assertValidCreatedHostedZone(HostedZone hostedZone)
 {
     Assert.AreEqual(ZONE_NAME, hostedZone.Name);
     Assert.IsNotNull(hostedZone.Id);
     Assert.AreEqual(COMMENT, hostedZone.Config.Comment);
 }
예제 #25
0
        internal CdkStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            var stackProps                = ReportStackProps.ParseOrDefault(props);
            var dframeWorkerLogGroup      = "MagicOnionBenchWorkerLogGroup";
            var dframeMasterLogGroup      = "MagicOnionBenchMasterLogGroup";
            var benchNetwork              = stackProps.GetBenchNetwork();
            var recreateMagicOnionTrigger = stackProps.GetBenchmarkServerBinariesHash();

            // s3
            var s3 = new Bucket(this, "Bucket", new BucketProps
            {
                AutoDeleteObjects = true,
                RemovalPolicy     = RemovalPolicy.DESTROY,
                AccessControl     = BucketAccessControl.PRIVATE,
            });
            var lifecycleRule = new LifecycleRule
            {
                Enabled    = true,
                Prefix     = "reports/",
                Expiration = Duration.Days(stackProps.DaysKeepReports),
                AbortIncompleteMultipartUploadAfter = Duration.Days(1),
            };

            s3.AddLifecycleRule(lifecycleRule);
            s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps
            {
                Sid        = "AllowPublicRead",
                Effect     = Effect.ALLOW,
                Principals = new[] { new AnyPrincipal() },
                Actions    = new[] { "s3:GetObject*" },
                Resources  = new[] { $"{s3.BucketArn}/html/*" },
            }));
            s3.AddToResourcePolicy(new PolicyStatement(new PolicyStatementProps
            {
                Sid        = "AllowAwsAccountAccess",
                Effect     = Effect.ALLOW,
                Principals = new[] { new AccountRootPrincipal() },
                Actions    = new[] { "s3:*" },
                Resources  = new[] { $"{s3.BucketArn}/*" },
            }));

            // s3 deploy
            var masterDllDeployment = new BucketDeployment(this, "DeployMasterDll", new BucketDeploymentProps
            {
                DestinationBucket    = s3,
                Sources              = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), $"out/linux/server")) },
                DestinationKeyPrefix = $"assembly/linux/server"
            });
            var userdataDeployment = new BucketDeployment(this, "UserData", new BucketDeploymentProps
            {
                DestinationBucket    = s3,
                Sources              = new[] { Source.Asset(Path.Combine(Directory.GetCurrentDirectory(), "userdata/")) },
                DestinationKeyPrefix = "userdata/"
            });

            // docker deploy
            var dockerImage = new DockerImageAsset(this, "dframeWorkerImage", new DockerImageAssetProps
            {
                Directory = Path.Combine(Directory.GetCurrentDirectory(), "app"),
                File      = "ConsoleAppEcs/Dockerfile.Ecs",
            });
            var dframeImage = ContainerImage.FromDockerImageAsset(dockerImage);

            // network
            var vpc = new Vpc(this, "Vpc", new VpcProps
            {
                MaxAzs              = 2,
                NatGateways         = 0,
                SubnetConfiguration = new[] { new SubnetConfiguration {
                                                  Name = "public", SubnetType = SubnetType.PUBLIC
                                              } },
            });
            var allsubnets = new SubnetSelection {
                Subnets = vpc.PublicSubnets
            };
            var singleSubnets = new SubnetSelection {
                Subnets = new[] { vpc.PublicSubnets.First() }
            };
            var sg = new SecurityGroup(this, "MasterSg", new SecurityGroupProps
            {
                AllowAllOutbound = true,
                Vpc = vpc,
            });

            foreach (var subnet in vpc.PublicSubnets)
            {
                sg.AddIngressRule(Peer.Ipv4(vpc.VpcCidrBlock), Port.AllTcp(), "VPC", true);
            }

            // service discovery
            var serviceDiscoveryDomain = "local";
            var serverMapName          = "server";
            var dframeMapName          = "dframe-master";
            var ns = new PrivateDnsNamespace(this, "Namespace", new PrivateDnsNamespaceProps
            {
                Vpc  = vpc,
                Name = serviceDiscoveryDomain,
            });
            var serviceDiscoveryServer = ns.CreateService("server", new DnsServiceProps
            {
                Name          = serverMapName,
                DnsRecordType = DnsRecordType.A,
                RoutingPolicy = RoutingPolicy.MULTIVALUE,
            });

            // alb
            var albDnsName = "benchmark-alb";
            var benchToMagicOnionDnsName = benchNetwork.RequireAlb
                ? $"{benchNetwork.EndpointScheme}://{albDnsName}.{stackProps.AlbDomain.domain}"
                : $"{benchNetwork.EndpointScheme}://{serverMapName}.{serviceDiscoveryDomain}";
            IApplicationTargetGroup grpcTargetGroup  = null;
            IApplicationTargetGroup httpsTargetGroup = null;

            if (benchNetwork.RequireAlb)
            {
                // route53
                var hostedZone = HostedZone.FromHostedZoneAttributes(this, "HostedZone", new HostedZoneAttributes
                {
                    HostedZoneId = stackProps.AlbDomain.zoneId,
                    ZoneName     = stackProps.AlbDomain.domain,
                });

                // acm
                var certificate = new DnsValidatedCertificate(this, "certificate", new DnsValidatedCertificateProps
                {
                    DomainName = $"{albDnsName}.{hostedZone.ZoneName}",
                    HostedZone = hostedZone,
                });
                // alb
                var lb = new ApplicationLoadBalancer(this, "LB", new ApplicationLoadBalancerProps
                {
                    Vpc           = vpc,
                    VpcSubnets    = allsubnets,
                    SecurityGroup = new SecurityGroup(this, "AlbSg", new SecurityGroupProps
                    {
                        AllowAllOutbound = true,
                        Vpc = vpc,
                    }),
                    InternetFacing = false,
                    Http2Enabled   = true,
                });
                grpcTargetGroup  = AddGrpcTargetGroup(benchNetwork, vpc, certificate, lb);
                httpsTargetGroup = AddHttpsTargetGroup(benchNetwork, vpc, certificate, lb);

                // Dns Record
                _ = new CnameRecord(this, "alb-alias-record", new CnameRecordProps
                {
                    RecordName = $"{albDnsName}.{stackProps.AlbDomain.domain}",
                    Ttl        = Duration.Seconds(60),
                    Zone       = hostedZone,
                    DomainName = lb.LoadBalancerDnsName,
                });
            }

            // iam
            var iamEc2MagicOnionRole  = GetIamEc2MagicOnionRole(s3, serviceDiscoveryServer);
            var iamEcsTaskExecuteRole = GetIamEcsTaskExecuteRole(new[] { dframeWorkerLogGroup, dframeMasterLogGroup });
            var iamDFrameTaskDefRole  = GetIamEcsDframeTaskDefRole(s3);
            var iamWorkerTaskDefRole  = GetIamEcsWorkerTaskDefRole(s3);

            // secrets
            var ddToken = stackProps.UseEc2DatadogAgentProfiler || stackProps.UseFargateDatadogAgentProfiler
                ? Amazon.CDK.AWS.SecretsManager.Secret.FromSecretNameV2(this, "dd-token", "magiconion-benchmark-datadog-token")
                : null;

            // MagicOnion
            var asg = new AutoScalingGroup(this, "MagicOnionAsg", new AutoScalingGroupProps
            {
                // Monitoring is default DETAILED.
                SpotPrice                = "1.0", // 0.0096 for spot price average for m3.medium
                Vpc                      = vpc,
                SecurityGroup            = sg,
                VpcSubnets               = singleSubnets,
                InstanceType             = stackProps.MagicOnionInstanceType,
                DesiredCapacity          = 1,
                MaxCapacity              = 1,
                MinCapacity              = 0,
                AssociatePublicIpAddress = true,
                MachineImage             = new AmazonLinuxImage(new AmazonLinuxImageProps
                {
                    CpuType        = AmazonLinuxCpuType.X86_64,
                    Generation     = AmazonLinuxGeneration.AMAZON_LINUX_2,
                    Storage        = AmazonLinuxStorage.GENERAL_PURPOSE,
                    Virtualization = AmazonLinuxVirt.HVM,
                }),
                AllowAllOutbound = true,
                GroupMetrics     = new[] { GroupMetrics.All() },
                Role             = iamEc2MagicOnionRole,
                UpdatePolicy     = UpdatePolicy.ReplacingUpdate(),
                Signals          = Signals.WaitForCount(1, new SignalsOptions
                {
                    Timeout = Duration.Minutes(10),
                }),
            });

            asg.AddSecretsReadGrant(ddToken, () => stackProps.UseEc2DatadogAgentProfiler);
            var userdata = GetUserData(recreateMagicOnionTrigger, s3.BucketName, stackProps.BenchmarkBinaryNames, serviceDiscoveryServer.ServiceId, stackProps.UseEc2CloudWatchAgentProfiler, stackProps.UseEc2DatadogAgentProfiler);

            asg.AddUserData(userdata);
            asg.UserData.AddSignalOnExitCommand(asg);
            asg.Node.AddDependency(masterDllDeployment);
            asg.Node.AddDependency(userdataDeployment);
            if (stackProps.EnableMagicOnionScaleInCron)
            {
                asg.ScaleOnSchedule("ScheduleOut", new BasicScheduledActionProps
                {
                    DesiredCapacity = 1,
                    MaxCapacity     = 1,
                    // AM9:00 (JST+9) on Monday to Wednesday
                    Schedule = Schedule.Expression("0 0 * 1-3 *"),
                });
                asg.ScaleOnSchedule("ScheduleIn", new BasicScheduledActionProps
                {
                    DesiredCapacity = 0,
                    MaxCapacity     = 0,
                    // PM9:00 (JST+9) on Everyday
                    Schedule = Schedule.Expression("0 12 * 1-7 *"),
                });
            }
            if (benchNetwork.RequireAlb)
            {
                asg.AttachToApplicationTargetGroup(grpcTargetGroup);
                asg.AttachToApplicationTargetGroup(httpsTargetGroup);
            }

            // ECS
            var cluster = new Cluster(this, "WorkerCluster", new ClusterProps
            {
                Vpc = vpc,
            });

            cluster.Node.AddDependency(asg); // wait until asg is up

            // dframe-worker
            var dframeWorkerContainerName = "worker";
            var dframeWorkerTaskDef       = new FargateTaskDefinition(this, "DFrameWorkerTaskDef", new FargateTaskDefinitionProps
            {
                ExecutionRole  = iamEcsTaskExecuteRole,
                TaskRole       = iamWorkerTaskDefRole,
                Cpu            = stackProps.WorkerFargate.CpuSize,
                MemoryLimitMiB = stackProps.WorkerFargate.MemorySize,
            });

            dframeWorkerTaskDef.AddContainer(dframeWorkerContainerName, new ContainerDefinitionOptions
            {
                Image       = dframeImage,
                Command     = new[] { "--worker-flag" },
                Environment = new Dictionary <string, string>
                {
                    { "DFRAME_MASTER_CONNECT_TO_HOST", $"{dframeMapName}.{serviceDiscoveryDomain}" },
                    { "DFRAME_MASTER_CONNECT_TO_PORT", "12345" },
                    { "BENCH_SERVER_HOST", benchToMagicOnionDnsName },
                    { "BENCH_REPORTID", stackProps.ReportId },
                    { "BENCH_S3BUCKET", s3.BucketName },
                },
                Logging = LogDriver.AwsLogs(new AwsLogDriverProps
                {
                    LogGroup = new LogGroup(this, "WorkerLogGroup", new LogGroupProps
                    {
                        LogGroupName  = dframeWorkerLogGroup,
                        RemovalPolicy = RemovalPolicy.DESTROY,
                        Retention     = RetentionDays.TWO_WEEKS,
                    }),
                    StreamPrefix = dframeWorkerLogGroup,
                }),
            });
            dframeWorkerTaskDef.AddDatadogContainer($"{dframeWorkerContainerName}-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler);
            var dframeWorkerService = new FargateService(this, "DFrameWorkerService", new FargateServiceProps
            {
                ServiceName       = "DFrameWorkerService",
                DesiredCount      = 0,
                Cluster           = cluster,
                TaskDefinition    = dframeWorkerTaskDef,
                VpcSubnets        = singleSubnets,
                SecurityGroups    = new[] { sg },
                PlatformVersion   = FargatePlatformVersion.VERSION1_4,
                MinHealthyPercent = 0,
                AssignPublicIp    = true,
            });

            // dframe-master
            var dframeMasterTaskDef = new FargateTaskDefinition(this, "DFrameMasterTaskDef", new FargateTaskDefinitionProps
            {
                ExecutionRole  = iamEcsTaskExecuteRole,
                TaskRole       = iamDFrameTaskDefRole,
                Cpu            = stackProps.MasterFargate.CpuSize,
                MemoryLimitMiB = stackProps.MasterFargate.MemorySize,
            });

            dframeMasterTaskDef.AddContainer("dframe", new ContainerDefinitionOptions
            {
                Image       = dframeImage,
                Environment = new Dictionary <string, string>
                {
                    { "DFRAME_CLUSTER_NAME", cluster.ClusterName },
                    { "DFRAME_MASTER_SERVICE_NAME", "DFrameMasterService" },
                    { "DFRAME_WORKER_CONTAINER_NAME", dframeWorkerContainerName },
                    { "DFRAME_WORKER_SERVICE_NAME", dframeWorkerService.ServiceName },
                    { "DFRAME_WORKER_TASK_NAME", Fn.Select(1, Fn.Split("/", dframeWorkerTaskDef.TaskDefinitionArn)) },
                    { "DFRAME_WORKER_IMAGE", dockerImage.ImageUri },
                    { "BENCH_REPORTID", stackProps.ReportId },
                    { "BENCH_S3BUCKET", s3.BucketName },
                },
                Logging = LogDriver.AwsLogs(new AwsLogDriverProps
                {
                    LogGroup = new LogGroup(this, "MasterLogGroup", new LogGroupProps
                    {
                        LogGroupName  = dframeMasterLogGroup,
                        RemovalPolicy = RemovalPolicy.DESTROY,
                        Retention     = RetentionDays.TWO_WEEKS,
                    }),
                    StreamPrefix = dframeMasterLogGroup,
                }),
            });
            dframeMasterTaskDef.AddDatadogContainer($"dframe-datadog", ddToken, () => stackProps.UseFargateDatadogAgentProfiler);
            var dframeMasterService = new FargateService(this, "DFrameMasterService", new FargateServiceProps
            {
                ServiceName       = "DFrameMasterService",
                DesiredCount      = 1,
                Cluster           = cluster,
                TaskDefinition    = dframeMasterTaskDef,
                VpcSubnets        = singleSubnets,
                SecurityGroups    = new[] { sg },
                PlatformVersion   = FargatePlatformVersion.VERSION1_4,
                MinHealthyPercent = 0,
                AssignPublicIp    = true,
            });

            dframeMasterService.EnableCloudMap(new CloudMapOptions
            {
                CloudMapNamespace = ns,
                Name          = dframeMapName,
                DnsRecordType = DnsRecordType.A,
                DnsTtl        = Duration.Seconds(300),
            });

            // output
            new CfnOutput(this, "ReportUrl", new CfnOutputProps {
                Value = $"https://{s3.BucketRegionalDomainName}/html/{stackProps.ReportId}/index.html"
            });
            new CfnOutput(this, "EndPointStyle", new CfnOutputProps {
                Value = stackProps.BenchmarkEndpoint.ToString()
            });
            new CfnOutput(this, "AsgName", new CfnOutputProps {
                Value = asg.AutoScalingGroupName
            });
            new CfnOutput(this, "EcsClusterName", new CfnOutputProps {
                Value = cluster.ClusterName
            });
            new CfnOutput(this, "DFrameWorkerEcsTaskdefImage", new CfnOutputProps {
                Value = dockerImage.ImageUri
            });
        }
        private async Task <HostedZone> FindHostedZoneAsync(string dnsName)
        {
            _logger.LogDebug("Finding hosted zone responsible for {DnsName}", dnsName);

            var partsToMatch      = dnsName.Split(DomainSegmentSeparator);
            var isWildcard        = dnsName.StartsWith(WildcardPrefix);
            var bestPossibleScore = isWildcard ? partsToMatch.Length - 1 : partsToMatch.Length;

            HostedZone bestMatch      = null;
            int        bestMatchScore = 0;

            var normalizedDnsName = NormalizeDnsName(dnsName);

            // Enumerate all hosted zones until a match is found, or until all hosted zones have been enumerated
            var hostedZones = await _route53Client.ListHostedZonesAsync();

            do
            {
                foreach (var hostedZone in hostedZones.HostedZones)
                {
                    _logger.LogDebug("Checking zone {Zone}", hostedZone.Name);

                    if (normalizedDnsName.EndsWith(hostedZone.Name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        var hostedZoneParts = hostedZone.Name.Split(DomainSegmentSeparator);
                        var score           = hostedZoneParts.Length;

                        if (score == bestPossibleScore)
                        {
                            _logger.LogInformation("Exact match for {DnsName} found (zone {Zone})", dnsName, hostedZone.Name);

                            return(hostedZone);
                        }
                        else if (score > bestMatchScore)
                        {
                            _logger.LogDebug("Setting best match for {DnsName} to zone {Zone}", dnsName, hostedZone.Name);

                            bestMatch      = hostedZone;
                            bestMatchScore = score;
                        }
                    }
                }

                hostedZones = await _route53Client.ListHostedZonesAsync(
                    new ListHostedZonesRequest()
                {
                    Marker = hostedZones.Marker
                });
            } while (hostedZones.IsTruncated);

            if (bestMatch == null)
            {
                _logger.LogInformation("No zone match for {DnsName} found", dnsName);
            }
            else
            {
                _logger.LogInformation("Best match for {DnsName} found (zone {Zone})", dnsName, bestMatch.Name);
            }

            return(bestMatch);
        }
예제 #27
0
        public IvrStack(Construct scope, string stackId, StackProps stackProps, IvrSiteSchema schema, IEnumerable <SecurityGroupRule> securityGroupRules) : base(scope, stackId, stackProps)
        {
            IVpc vpc             = null;
            var  MaxIpsPerSubnet = IvrVpcProps.MaxIpsPerSubnet;

            if (!string.IsNullOrWhiteSpace(schema.VpcName))
            {
                vpc = Vpc.FromLookup(this, "$VPC", new VpcLookupOptions {
                    VpcName = schema.VpcName,
                });                                                                                     // will error if not found
                //MaxIpsPerSubnet = ???;
            }
            else if (!string.IsNullOrWhiteSpace(schema.VpcId))
            {
                vpc = Vpc.FromLookup(this, "$VPC", new VpcLookupOptions {
                    VpcId = schema.VpcId,
                });                                                                                 // will error if not found
                //MaxIpsPerSubnet = ???;
            }
            else if (null != schema.VpcProps)
            {
                // use provided props to create brand new VPC
                vpc = new IvrVpc(this, $"VPC", schema.VpcProps);
            }

            if (schema.AddVpcS3Gateway)
            {
                var s3gw = new GatewayVpcEndpoint(this, $"S3GW", new GatewayVpcEndpointProps
                {
                    Vpc     = vpc,
                    Service = GatewayVpcEndpointAwsService.S3,
                    Subnets = new SubnetSelection[] { new SubnetSelection {
                                                          SubnetType = SubnetType.PUBLIC,
                                                      } },
                });
            }

            var role = new Role(this, "IVR", new RoleProps
            {
                AssumedBy      = new ServicePrincipal("ec2.amazonaws.com"),
                InlinePolicies = new IvrInlinePolicies(stackProps.Env.Account, stackId, schema),
            });

            // Configure inbound security for RDP (and more?)
            var securityGroup = new SecurityGroup(this, $"Ingress", new SecurityGroupProps
            {
                Vpc = vpc,
                AllowAllOutbound = schema.AllowAllOutbound,
            });

            securityGroupRules.ForEach(rule => securityGroup.WithSecurityGroupRule(rule));
            if (schema.AllowAllIntranet)
            {
                securityGroup.WithSecurityGroupRule(new IngressRule(Peer.Ipv4($"{vpc.VpcCidrBlock}"), Port.AllTraffic()).WithDescription($"All intranet traffic"));
            }

            // Finally - create our instances!
            var hosts = new List <HostInstance>();

            for (var subnetIndex = 0; ++subnetIndex <= Math.Min(vpc.PublicSubnets.Length, schema.MaxSubnets);)
            {
                var hostIndexInSubnet = 0;
                foreach (var group in schema.HostGroups)
                {
                    var numberOfHosts = Math.Min(group.HostCount, MaxIpsPerSubnet);
                    if (numberOfHosts != group.HostCount)
                    {
                        Console.WriteLine($"Group({group.Name}) host count changed from {group.HostCount} to {numberOfHosts}");
                        group.HostCount = numberOfHosts;
                    }
                    var instanceProps = IvrInstanceProps.InstanceProps(vpc, vpc.PublicSubnets[subnetIndex - 1], role, securityGroup, group.InstanceProps);
                    for (var hostCount = 0; ++hostCount <= numberOfHosts; ++hostIndexInSubnet)
                    {
                        var hostName         = $"{schema.HostNamePrefix}{subnetIndex}{hostIndexInSubnet:00}";
                        var hostPrimingProps = new HostPrimingProps
                        {
                            HostName           = hostName.AsWindowsComputerName(), // must fit into 15 chars
                            WorkingFolder      = $"{stackId}".AsWindowsFolder(),
                            AwsAccount         = stackProps.Env.Account,
                            AwsRoleName        = role.RoleName,
                            RdpProps           = schema.RdpProps,
                            EC2Users           = schema.EC2Users,
                            DownloadAndInstall = group.DownloadAndInstall,
                            S3iArgs            = $"{group.InstallS3i} --verbose",
                        };
                        var hostCommands = HostPriming.PrimeForS3i(hostPrimingProps)
                                           .WithFirewallAllowInbound($"{vpc.VpcCidrBlock}");
                        hostCommands.WithRenameAndRestart(hostPrimingProps.HostName);
                        instanceProps.KeyName  = schema.KeyPairName;
                        instanceProps.UserData = hostCommands.UserData;
                        hosts.Add(new HostInstance
                        {
                            Group    = group,
                            Instance = new Instance_(this, hostName.AsCloudFormationId(), instanceProps),
                        });
                    }
                }
            }
            // associate pre-allocated EIPs
            var preAllocatedEIPs    = schema.PreAllocatedElasticIPs?.SelectMany(s => s.Csv()).ToList() ?? new List <string> {
            };
            var hostsThatRequireEIP = hosts.Where(h => h.Group.UsePreAllocatedElasticIPs).ToList();

            if (preAllocatedEIPs.Count < hostsThatRequireEIP.Count)
            {
                throw new ArgumentException($"Pre-Allocated Elastic IPs needed: {hostsThatRequireEIP.Count()}, but only {preAllocatedEIPs.Count()} configured in schema.{nameof(IvrSiteSchema.PreAllocatedElasticIPs)}");
            }
            var elasticIPAssociations = hostsThatRequireEIP.Zip(preAllocatedEIPs, (h, a) =>
            {
                return(new CfnEIPAssociation(this, $"EIPA{h.Instance.InstancePrivateIp}".AsCloudFormationId(), new CfnEIPAssociationProps
                {
                    AllocationId = a,
                    InstanceId = h.Instance.InstanceId,
                }));
            }).ToList();    // execute LINQ now

            // We have schema.Domain registered in advance
            if (!string.IsNullOrWhiteSpace(schema.HostedZoneDomain))
            {
                var theZone = HostedZone.FromLookup(this, $"{stackId}_Zone_", new HostedZoneProviderProps
                {
                    DomainName = schema.HostedZoneDomain,
                    //Comment = "HostedZone created by Route53 Registrar",
                });
                // assign new Elastic IPs as needed
                if (!string.IsNullOrWhiteSpace(schema.SubdomainEIPs))
                {
                    var newElasticIPs = hosts.Where(h => h.Group.AllocateNewElasticIPs).Select(h =>
                    {
                        return(new CfnEIP(this, $"EIP{h.Instance.InstancePrivateIp}".AsCloudFormationId(), new CfnEIPProps
                        {
                            // 'standard' or 'vpc': https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-domain
                            Domain = "vpc",
                            InstanceId = h.Instance.InstanceId,
                        }));
                    }).ToList();   // collect them now to prevent LINQ side effects
                    if (newElasticIPs.Any())
                    {
                        // Register public Elastic IPs
                        var arNewPublic = new ARecord(this, $"ARecord_Public_NewAlloc".AsCloudFormationId(), new ARecordProps
                        {
                            Zone       = theZone,
                            RecordName = $"{schema.SubdomainEIPs}.{theZone.ZoneName}",
                            Target     = RecordTarget.FromValues(newElasticIPs.Select(eip => eip.Ref).ToArray()),
                            Ttl        = Duration.Seconds(300),
                        });
                    }
                    else if (elasticIPAssociations.Any())
                    {
                        // Register public Elastic IPs

                        /*
                         * var arPrePublic = new ARecord(this, $"ARecord_Public_PreAlloc".AsCloudFormationId(), new ARecordProps
                         * {
                         *  Zone = theZone,
                         *  RecordName = $"{schema.SubdomainEIPs}.{theZone.ZoneName}",
                         *  Target = RecordTarget.FromValues(elasticIPAssociations.Select(eipa => eipa.Ref).ToArray()), // ***** how to do that?
                         *  Ttl = Duration.Seconds(300),
                         * });
                         */
                        foreach (var a in elasticIPAssociations)
                        {
                            Console.WriteLine($"Pre-Allocated Elastic IP Associations: {a.AllocationId}/{a.InstanceId}, {a.Eip}/{a.PrivateIpAddress}, {a.Ref} - please put it to {schema.SubdomainEIPs}.{theZone.ZoneName} ARecord manually");
                        }
                    }
                }
                if (0 < hosts.Count && !string.IsNullOrWhiteSpace(schema.SubdomainHosts))
                {
                    // Register private IPs (never changing, as opposed to public - which change on stop/start) addresses of all hosts
                    var arPrivate = new ARecord(this, $"ARecord_Private_".AsCloudFormationId(), new ARecordProps
                    {
                        Zone       = theZone,
                        RecordName = $"{schema.SubdomainHosts}.{theZone.ZoneName}",
                        Target     = RecordTarget.FromIpAddresses(hosts.Select(h => h.Instance.InstancePrivateIp).ToArray()),
                        Ttl        = Duration.Seconds(300),
                    });
                }
                //throw new Exception();
            }
        }
예제 #28
0
        public SpaStack(Construct scope, string id, SpaStackProps props) : base(scope, id, props)
        {
            //s3 bucket
            var bucket = new Bucket(this, $"{props.ServiceName}-bucket", new BucketProps
            {
                WebsiteIndexDocument = "index.html",
                Versioned            = true,
                BucketName           = props.ServiceName,
                RemovalPolicy        = RemovalPolicy.DESTROY
            });

            //cloudfront distribution
            var cloudFrontOai = new OriginAccessIdentity(this, $"{props.ServiceName}-oai", new OriginAccessIdentityProps
            {
                Comment = $"OAI for {props.ServiceName}."
            });

            var cloudfrontDist = new CloudFrontWebDistribution(this, $"{props.ServiceName}-cfd", new CloudFrontWebDistributionProps
            {
                ViewerCertificate = ViewerCertificate.FromAcmCertificate(
                    props.CloudFrontCert,
                    new ViewerCertificateOptions
                {
                    Aliases   = new [] { $"{props.SubDomain}.{props.HostedZoneName}" },
                    SslMethod = SSLMethod.SNI
                }),
                OriginConfigs = new ISourceConfiguration[]
                {
                    new SourceConfiguration
                    {
                        S3OriginSource = new S3OriginConfig
                        {
                            S3BucketSource       = bucket,
                            OriginAccessIdentity = cloudFrontOai
                        },
                        Behaviors = new IBehavior[]
                        {
                            new Behavior
                            {
                                IsDefaultBehavior = true,
                            }
                        }
                    }
                }
            });

            var cnameRecord = new CnameRecord(this, $"{props.ServiceName}CloudFrontCname", new CnameRecordProps
            {
                Zone = HostedZone.FromHostedZoneAttributes(this, "HostedZone", new HostedZoneAttributes
                {
                    ZoneName     = props.HostedZoneName,
                    HostedZoneId = props.HostedZoneId
                }),
                RecordName = props.SubDomain,
                DomainName = cloudfrontDist.DistributionDomainName
            });

            var cloudfrontS3Access = new PolicyStatement();

            cloudfrontS3Access.AddActions("s3:GetBucket*", "s3:GetObject*", "s3:List*");
            cloudfrontS3Access.AddResources(bucket.BucketArn);
            cloudfrontS3Access.AddResources($"{bucket.BucketArn}/*");
            cloudfrontS3Access.AddCanonicalUserPrincipal(cloudFrontOai.CloudFrontOriginAccessIdentityS3CanonicalUserId);

            bucket.AddToResourcePolicy(cloudfrontS3Access);

            //codebuild project

            var codeBuildProject = new Project(this, $"{props.ServiceName}-codeBuild-project", new ProjectProps
            {
                Vpc         = props.Vpc,
                ProjectName = props.ServiceName,
                Environment = new BuildEnvironment
                {
                    BuildImage = LinuxBuildImage.STANDARD_4_0,
                },
                Source               = Source.GitHub(props.GitHubSourceProps),
                BuildSpec            = BuildSpec.FromSourceFilename(props.BuildSpecFile),
                EnvironmentVariables = new Dictionary <string, IBuildEnvironmentVariable>
                {
                    { "SPA_DIRECTORY", new BuildEnvironmentVariable {
                          Value = props.SpaDirectory
                      } },
                    { "S3_BUCKET", new BuildEnvironmentVariable {
                          Value = bucket.BucketName
                      } },
                    { "CLOUDFRONT_ID", new BuildEnvironmentVariable {
                          Value = cloudfrontDist.DistributionId
                      } },
                    { "API_URL", new BuildEnvironmentVariable {
                          Value = props.ApiUrl
                      } }
                }
            });

            // iam policy to push your build to S3
            codeBuildProject.AddToRolePolicy(
                new PolicyStatement(new PolicyStatementProps
            {
                Effect    = Effect.ALLOW,
                Resources = new[] { bucket.BucketArn, $"{bucket.BucketArn}/*" },
                Actions   = new[]
                {
                    "s3:GetBucket*",
                    "s3:List*",
                    "s3:GetObject*",
                    "s3:DeleteObject",
                    "s3:PutObject"
                }
            }));

            codeBuildProject.AddToRolePolicy(
                new PolicyStatement(new PolicyStatementProps
            {
                Effect    = Effect.ALLOW,
                Resources = new [] { "*" },
                Actions   = new []
                {
                    "cloudfront:CreateInvalidation",
                    "cloudfront:GetDistribution*",
                    "cloudfront:GetInvalidation",
                    "cloudfront:ListInvalidations",
                    "cloudfront:ListDistributions"
                }
            }));

            //codepipeline?
        }
        // A simple construct that contains a collection of AWS S3 buckets.
        public StaticSiteConstruct(Construct scope, string id, StaticSiteConstructProps props) : base(scope, id)
        {
            var zone = HostedZone.FromLookup(this, "Zone", new HostedZoneProviderProps
            {
                DomainName = props.DomainName
            });

            var siteDomain = (string)($"{props.SiteSubDomain}.{props.DomainName}");

            new CfnOutput(this, "Site", new CfnOutputProps
            {
                Value = $"https://{siteDomain}"
            });

            var siteBucket = new Bucket(this, "SiteBucket", new BucketProps
            {
                BucketName           = siteDomain,
                WebsiteIndexDocument = "index.html",
                WebsiteErrorDocument = "error.html",
                PublicReadAccess     = true,

                // The default removal policy is RETAIN, which means that cdk destroy will not attempt to delete
                // the new bucket, and it will remain in your account until manually deleted. By setting the policy to
                // DESTROY, cdk destroy will attempt to delete the bucket, but will error if the bucket is not empty.
                RemovalPolicy = RemovalPolicy.DESTROY // NOT recommended for production code
            });

            new CfnOutput(this, "Bucket", new CfnOutputProps
            {
                Value = siteBucket.BucketName
            });

            var certificateArn = new DnsValidatedCertificate(this, "SiteCertificate", new DnsValidatedCertificateProps
            {
                DomainName = siteDomain,
                HostedZone = zone
            }).CertificateArn;

            new CfnOutput(this, "Certificate", new CfnOutputProps {
                Value = certificateArn
            });

            var behavior = new Behavior();

            behavior.IsDefaultBehavior = true;

            var distribution = new CloudFrontWebDistribution(this, "SiteDistribution", new CloudFrontWebDistributionProps
            {
                AliasConfiguration = new AliasConfiguration
                {
                    AcmCertRef     = certificateArn,
                    Names          = new string[] { siteDomain },
                    SslMethod      = SSLMethod.SNI,
                    SecurityPolicy = SecurityPolicyProtocol.TLS_V1_2016
                },
                OriginConfigs = new ISourceConfiguration[]
                {
                    new SourceConfiguration
                    {
                        S3OriginSource = new S3OriginConfig
                        {
                            S3BucketSource = siteBucket
                        },
                        Behaviors = new Behavior[] { behavior }
                    }
                }
            });

            new CfnOutput(this, "DistributionId", new CfnOutputProps
            {
                Value = distribution.DistributionId
            });

            new ARecord(this, "SiteAliasRecord", new ARecordProps
            {
                RecordName = siteDomain,
                Target     = RecordTarget.FromAlias(new CloudFrontTarget(distribution)),
                Zone       = zone
            });

            new BucketDeployment(this, "DeployWithInvalidation", new BucketDeploymentProps
            {
                Sources           = new ISource[] { Source.Asset("./site-contents") },
                DestinationBucket = siteBucket,
                Distribution      = distribution,
                DistributionPaths = new string[] { "/*" }
            });
        }
예제 #30
0
 private async Task DeleteRoute53RecordSet(HostedZone zone, Instance instance, ILambdaContext context)
 {
     context.Logger.LogLine($"**** TODO: Delete route53 record set!");
     // TODO:
 }