def entry(hostname, i): if _is_domain_2nd_level(hostname): hostedzone = hostname + "." ip_addresses = context['fastly']['dns']['a'] return route53.RecordSetType( R53_FASTLY_TITLE % ( i + 1 ), # expecting more than one entry (aliases), so numbering them immediately HostedZoneName=hostedzone, Name=hostname, Type="A", TTL="60", ResourceRecords=ip_addresses, ) raise ConfigurationError( "2nd-level domains aliases are not supported yet by builder. See https://docs.fastly.com/guides/basic-configuration/using-fastly-with-apex-domains" ) hostedzone = context['domain'] + "." cname = context['fastly']['dns']['cname'] return route53.RecordSetType( R53_FASTLY_TITLE % ( i + 1 ), # expecting more than one entry (aliases), so numbering them immediately HostedZoneName=hostedzone, Name=hostname, Type="CNAME", TTL="60", ResourceRecords=[cname], )
def entry(hostname, i): if _is_domain_2nd_level(hostname): # must be an alias as it is a 2nd-level domain like elifesciences.net hostedzone = hostname + "." ensure(context['elb'], "2nd-level domains aliases are only supported for ELBs") return route53.RecordSetType(R53_CNAME_TITLE % (i + 1), HostedZoneName=hostedzone, Name=hostname, Type="A", AliasTarget=route53.AliasTarget( GetAtt( ELB_TITLE, "CanonicalHostedZoneNameID"), GetAtt(ELB_TITLE, "DNSName"))) else: hostedzone = context['domain'] + "." return route53.RecordSetType( R53_CNAME_TITLE % (i + 1), HostedZoneName=hostedzone, Name=hostname, Type="CNAME", TTL="60", ResourceRecords=[context['full_hostname']], )
def render_ec2_dns(context, template): # single ec2 node may get an external hostname if context['full_hostname'] and not context['elb']: ensure( context['ec2']['cluster-size'] <= 1, "If there is no load balancer, multiple EC2 instances cannot be assigned a single DNS entry" ) if context['ec2']['cluster-size'] == 1: template.add_resource(external_dns_ec2_single(context)) [template.add_resource(cname) for cname in cnames(context)] # single ec2 node may get an internal hostname if context['int_full_hostname'] and not context['elb']: ensure( context['ec2']['cluster-size'] == 1, "If there is no load balancer, only a single EC2 instance can be assigned a DNS entry" ) template.add_resource(internal_dns_ec2_single(context)) # ec2 nodes in a cluster may get a different internal hostname each if context['ec2']['dns-internal']: for node in range(1, context['ec2']['cluster-size'] + 1): hostedzone = context['int_domain'] + "." dns_record = route53.RecordSetType( R53_INT_TITLE_NODE % node, HostedZoneName=hostedzone, Comment="Internal DNS record for EC2 node %s" % node, Name=context['int_node_hostname'] % node, Type="A", TTL="60", ResourceRecords=[GetAtt(EC2_TITLE_NODE % node, "PrivateIp")], ) template.add_resource(dns_record) # primary ec2 node in a cluster may get an external hostname if context['domain'] and context['ec2']['dns-external-primary']: hostedzone = context['domain'] + "." primary = 1 dns_record = route53.RecordSetType( R53_EXT_TITLE_NODE % primary, HostedZoneName=hostedzone, Comment="External DNS record for EC2 primary", Name=context['ext_node_hostname'] % primary, Type="A", TTL="60", ResourceRecords=[GetAtt(EC2_TITLE_NODE % primary, "PublicIp")], ) template.add_resource(dns_record)
def resources(self, stack: Stack) -> list[AWSObject]: """Return list of AWSObject associated with the construct.""" # Add bucket policy granting read access to te cloudfront distribution self.add_oai_access_to_bucket() result = [ *self.bucket.resources(stack), self.cache_policy, self.distribution, self.origin_access_identity, ] # Add a lambda invalidating cloudfront cache when bucket objects are modified result.extend(self.add_cache_invalidation(stack)) # Add route53 records if needed if self.r53_route_from: for zone_id, domain in self.r53_route_from: result.append( route53.RecordSetType( name_to_id(f"{self.name}-{domain}-r53-rset"), AliasTarget=route53.AliasTarget( DNSName=self.domain_name, # Z2FDTNDATAQYW2 is always the hosted zone ID when you # create an alias record that routes traffic to a # CloudFront distribution HostedZoneId="Z2FDTNDATAQYW2", ), Name=domain, HostedZoneId=zone_id, Type="A", )) return result
def declare_domain(self, domain_name: str, hosted_zone_id: str, stage_name: str) -> list[AWSObject]: """Declare a custom domain for one of the API stage. Note that when a custom domain is created then a certificate is automatically created for that domain. :param domain_name: domain name :param hosted_zone_id: hosted zone in which the domain belongs to :param stage_name: stage that should be associated with that domain :return: a list of AWSObject """ result = [] certificate_id = name_to_id(self.name + domain_name + "Certificate") certificate = Certificate( certificate_id, DomainName=domain_name, DomainValidationOptions=[ DomainValidationOption(DomainName=domain_name, HostedZoneId=hosted_zone_id) ], ValidationMethod="DNS", ) result.append(certificate) domain = apigatewayv2.DomainName( name_to_id(self.name + domain_name + "Domain"), DomainName=domain_name, DomainNameConfigurations=[ apigatewayv2.DomainNameConfiguration( CertificateArn=certificate.ref()) ], ) result.append(domain) result.append( apigatewayv2.ApiMapping( name_to_id(self.name + domain_name + "ApiMapping"), DomainName=domain.ref(), ApiId=self.ref, Stage=self.stage_ref(stage_name), )) result.append( route53.RecordSetType( name_to_id(self.name + domain_name + "DNS"), Name=domain_name, Type="A", HostedZoneId=hosted_zone_id, AliasTarget=route53.AliasTarget( DNSName=GetAtt( name_to_id(self.name + domain_name + "Domain"), "RegionalDomainName", ), HostedZoneId=GetAtt( name_to_id(self.name + domain_name + "Domain"), "RegionalHostedZoneId", ), EvaluateTargetHealth=False, ), )) return result
def public_record_set(self, index): return route53.RecordSetType( 'PilosaPublicRecordSet{}'.format(index), HostedZoneName='{domain}.'.format(domain=self.domain), Name=Join('', ['node{}.'.format(index), Ref(self.cluster_name), '.{domain}.'.format(domain=self.domain)]), Type="A", TTL="300", ResourceRecords=[GetAtt("PilosaInstance{}".format(index), "PublicIp")], )
def agent_private_record_set(self, index): return route53.RecordSetType( 'AgentPrivateRecordSet{}'.format(index), HostedZoneId=Ref(self.hosted_zone), Name=Join('', ['agent{}.'.format(index), Ref(self.cluster_name), '.{domain}.'.format(domain=self.domain)]), Type="A", TTL="300", ResourceRecords=[GetAtt("PilosaAgentInstance{}".format(index), "PrivateIp")], )
def simple2resource(record, zone_name): name = get_resource_name(record) resource = route53.RecordSetType(name) resource.HostedZoneName = zone_name.ref() resource.Name = record['Name'] resource.TTL = record['TTL'] resource.ResourceRecords = [l['Value'] for l in record['ResourceRecords']] resource.Type = record['Type'] return resource
def add_route53(self, stack_name, template, provision_refs): template.add_resource( route53.RecordSetType( "NetworkDNSRecord", HostedZoneName="network.shipchain.io.", Comment=f"DNS name for {stack_name} network ALB", Name=f"{stack_name}.network.shipchain.io.", Type="A", AliasTarget=route53.AliasTarget( DNSName=GetAtt(provision_refs.alb, 'DNSName'), HostedZoneId=GetAtt(provision_refs.alb, "CanonicalHostedZoneID"))))
def add_s3_dns(self): name, tags = self._name_tags('s3_dns') self.s3_dns = self.t.add_resource( route53.RecordSetType( name, HostedZoneName=self.aws['s3_dns.zone'], Comment='CNAME to public ELB', Name=self.aws['s3_dns.record'] + '.' + self.aws['s3_dns.zone'], TTL=self.aws['s3_dns.ttl'], Type='CNAME', ResourceRecords=[GetAtt(self.s3.name, 'DomainName')], # Doesn't support: Tags=Tags(**tags), ))
def internal_dns_ec2_single(context): # The DNS name of an existing Amazon Route 53 hosted zone hostedzone = context['int_domain'] + "." # TRAILING DOT IS IMPORTANT! dns_record = route53.RecordSetType( R53_INT_TITLE, HostedZoneName=hostedzone, Comment="Internal DNS record for EC2", Name=context['int_full_hostname'] + '.', Type="A", TTL="60", ResourceRecords=[GetAtt(EC2_TITLE, "PrivateIp")], ) return dns_record
def alias2resource(record, zone_name): name = get_resource_name(record, suffix='ALIAS') resource = route53.RecordSetType(name) resource.HostedZoneName = zone_name.ref() resource.Name = record['Name'] resource.Type = record['Type'] alias_target = route53.AliasTarget() alias_target.HostedZoneId = record['AliasTarget']['HostedZoneId'] alias_target.DNSName = record['AliasTarget']['DNSName'] alias_target.EvaluateTargetHealth = record['AliasTarget'][ 'EvaluateTargetHealth'] resource.AliasTarget = alias_target return resource
def internal_dns_elb(context): # The DNS name of an existing Amazon Route 53 hosted zone hostedzone = context['int_domain'] + "." # TRAILING DOT IS IMPORTANT! dns_record = route53.RecordSetType(R53_INT_TITLE, HostedZoneName=hostedzone, Comment="Internal DNS record for ELB", Name=context['int_full_hostname'], Type="A", AliasTarget=route53.AliasTarget( GetAtt(ELB_TITLE, "CanonicalHostedZoneNameID"), GetAtt(ELB_TITLE, "DNSName"))) return dns_record
def external_dns_ec2(context): # The DNS name of an existing Amazon Route 53 hosted zone hostedzone = context['domain'] + "." # TRAILING DOT IS IMPORTANT! dns_record = route53.RecordSetType( R53_EXT_TITLE, HostedZoneName=hostedzone, Comment="External DNS record for EC2", Name=context['full_hostname'], Type="A", TTL="60", ResourceRecords=[GetAtt(EC2_TITLE, "PublicIp")], ) return dns_record
def external_dns_elb(context): # http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html # The DNS name of an existing Amazon Route 53 hosted zone hostedzone = context['domain'] + "." # TRAILING DOT IS IMPORTANT! dns_record = route53.RecordSetType(R53_EXT_TITLE, HostedZoneName=hostedzone, Comment="External DNS record for ELB", Name=context['full_hostname'], Type="A", AliasTarget=route53.AliasTarget( GetAtt(ELB_TITLE, "CanonicalHostedZoneNameID"), GetAtt(ELB_TITLE, "DNSName"))) return dns_record
def create_dns_records(self): t = self.template t.add_resource( route53.RecordSetType( DNS_RECORD, HostedZoneId=Ref('InternalZoneId'), Comment='ES Domain CNAME Record', Name=Join('.', [Ref('InternalHostname'), Ref('InternalZoneName')]), Type='CNAME', TTL='120', ResourceRecords=[GetAtt('Domain', 'DomainEndpoint')], Condition='CreateInternalHostname', ) ) t.add_output(Output('CNAME', Condition='CreateInternalHostname', Value=Ref(DNS_RECORD)))
def register_elb_to_dns(self, elb, tier_name, tier_args): ''' Method handles the process of uniformly creating CNAME records for ELBs in a given tier @param elb [Troposphere.elasticloadbalancing.LoadBalancer] @param tier_name [str] @param tier_args [dict] ''' if 'environmentHostedZone' not in self.template.parameters: hostedzone = self.template.add_parameter( Parameter( "environmentHostedZone", Description= "The DNS name of an existing Amazon Route 53 hosted zone", Default=tier_args.get('base_hosted_zone_name', 'devopsdemo.com'), Type="String")) else: hostedzone = self.template.parameters.get('environmentHostedZone') if tier_name.lower() + 'HostName' not in self.template.parameters: host_name = self.template.add_parameter( Parameter( tier_name.lower() + 'HostName', Description= "Friendly host name to append to the environmentHostedZone base DNS record", Type="String", Default=tier_args.get('tier_host_name', tier_name.lower()))) else: host_name = self.template.parameters.get(tier_name.lower() + 'HostName') self.template.add_resource( r53.RecordSetType( tier_name.lower() + 'DnsRecord', HostedZoneName=Join('', [Ref(hostedzone), '.']), Comment='CNAME record for ' + tier_name.capitalize() + ' tier', Name=Join( '', [Ref(host_name), '.', Ref(hostedzone)]), Type='CNAME', TTL='300', ResourceRecords=[GetAtt(elb, 'DNSName')]))
def add_elb(self): self.ElasticLoadBalancer = self.template.add_resource( elb.LoadBalancer( "ElbWeb", Subnets=[Ref(self.Subnet1), Ref(self.Subnet2)], Listeners=[{ "InstancePort": "80", "LoadBalancerPort": "80", "Protocol": "HTTP" }], CrossZone="true", LoadBalancerName=Join("-", ["elb", Ref(self.Project)]), SecurityGroups=[Ref(self.ElbSecurityGroup)], ConnectionDrainingPolicy=elb.ConnectionDrainingPolicy( Enabled=True, Timeout=300, ), HealthCheck=elb.HealthCheck( HealthyThreshold="3", Interval="30", Target="HTTP:80/", Timeout="5", UnhealthyThreshold="5", ), Tags=Tags( Name=Join("-", ["ELB", Ref(self.Project)]), Environment=Ref(self.Environment), ), )) self.ELBcname = self.template.add_resource( route53.RecordSetType( "ELBcname", HostedZoneName=Join("", [Ref(self.Domain), "."]), Comment="CNAME to Web ELB", Name=Join( ".", [Ref(self.Hostname), Ref(self.Domain)]), Type="CNAME", TTL="60", ResourceRecords=[GetAtt(self.ElasticLoadBalancer, "DNSName")]))
def create_dns_record(self): t = self.template variables = self.get_variables() should_create_dns = all([ variables["InternalZoneId"], variables["InternalZoneName"], variables["InternalHostName"], ]) if should_create_dns: t.add_resource( route53.RecordSetType( DNS_RECORD, HostedZoneId=variables["InternalZoneId"], Comment="ES Domain CNAME Record", Name="{}.{}".format(variables["InternalHostName"], variables["InternalZoneName"]), Type="CNAME", TTL="120", ResourceRecords=[GetAtt(ES_DOMAIN, "DomainEndpoint")], )) t.add_output(Output("CNAME", Value=Ref(DNS_RECORD)))
def external_dns_cloudfront(context): # http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html dns_records = [] i = 1 for cdn_hostname in context['cloudfront']['subdomains']: if _is_domain_2nd_level(cdn_hostname): hostedzone = cdn_hostname + "." else: hostedzone = context['domain'] + "." dns_records.append( route53.RecordSetType( R53_CDN_TITLE % i, HostedZoneName=hostedzone, Comment="External DNS record for Cloudfront distribution", Name=cdn_hostname + ".", Type="A", AliasTarget=route53.AliasTarget( CLOUDFRONT_HOSTED_ZONE_ID, GetAtt(CLOUDFRONT_TITLE, "DomainName")))) i = i + 1 return dns_records
def add_cname(self): """ Wrapper method to encapsulate process of creating a CNAME DNS record for the ELB Requires InternalHostedZone parameter Sets self.cname_record with the record resource """ if not self.cname: return hosted_zone = self.add_parameter( Parameter('InternalHostedZone', Description='Internal Hosted Zone Name', Type='String')) self.cname_record = self.add_resource( route53.RecordSetType( self.name.lower() + 'DnsRecord', HostedZoneId=Ref(hosted_zone), Comment='CNAME record for %s' % self.name, Name=self.cname, Type='CNAME', TTL='300', ResourceRecords=[GetAtt(self.cluster_elb, 'DNSName')]))
def __init__(self, title, template, single_instance_config): """ AWS CloudFormation - http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html Troposphere - https://github.com/cloudtools/troposphere/blob/master/troposphere/ec2.py Create a singleton instance such as a nat or a jumphost :param title: Title of instance e.g 'nat1', 'nat2' or 'jump1' :param template: The template to add the SingleInstance object to. :param single_instance_config: object containing variables specific to single instance configuration """ super(SingleInstance, self).__init__(vpc=single_instance_config.vpc, title=title, template=template) self.sns_topic = single_instance_config.sns_topic region = single_instance_config.availability_zone[:-1] userdata = """#cloud-config # Capture all cloud-config output into a more readable logfile output: {all: '| tee -a /var/log/cloud-init-output.log'} # update and install packages, reboot if necessary package_upgrade: true package_reboot_if_required: true packages: - perl-Switch - perl-DateTime - perl-Sys-Syslog - perl-LWP-Protocol-https write_files: - path: /etc/awslogs.cfg content: | [general] state_file = /var/awslogs/state/agent-state [/var/log/messages] file = /var/log/messages log_group_name = /var/log/messages log_stream_name = {instance_id} datetime_format = %b %d %H:%M:%S runcmd: # cloudwatch monitoring scripts - curl -so /tmp/CloudWatchMonitoringScripts-1.2.1.zip http://aws-cloudwatch.s3.amazonaws.com/downloads/CloudWatchMonitoringScripts-1.2.1.zip - unzip -d /opt /tmp/CloudWatchMonitoringScripts-1.2.1.zip - echo '*/5 * * * * root /opt/aws-scripts-mon/mon-put-instance-data.pl --mem-util --mem-used --mem-avail --disk-space-util --disk-path=/ --from-cron' > /etc/cron.d/cloudwatch # cloudwatch logs agent and forwarding config - curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O - chmod +x ./awslogs-agent-setup.py - ./awslogs-agent-setup.py -n -r """ + region + """ -c /etc/awslogs.cfg """ tags = Tags(Name=Join('', [Ref('AWS::StackName'), '-', title])) if single_instance_config.ec2_scheduled_shutdown: # Add tag to instance, so it gets picked up by EC2 Scheduler (a CF stack that needs to be running): # http://docs.aws.amazon.com/solutions/latest/ec2-scheduler/deployment.html # # EC2 scheduler tag format: "<start time>;<stop time>;utc;<active days>" # # The times are in UTC, so need to account for this: # 1900 UTC (previous day) = 0600 AEDT, 0900 UTC = 2000 AEDT, # therefore, it needs to run Sunday UTC to be Monday AEDT. # # A work day is defined as: 6am-9pm. (An additional hour is added before & after for daylight savings) tags += Tags(**{ 'scheduler:ec2-startstop': '1900;0900;utc;sun,mon,tue,wed,thu' }) self.single = self.template.add_resource( ec2.Instance( title, KeyName=single_instance_config.keypair, ImageId=single_instance_config.si_image_id, InstanceType=single_instance_config.si_instance_type, NetworkInterfaces=[ ec2.NetworkInterfaceProperty( GroupSet=[self.security_group], AssociatePublicIpAddress=True, DeviceIndex='0', DeleteOnTermination=True, SubnetId=single_instance_config.subnet, ) ], # The below boolean determines whether source/destination checking is enabled on the # instance. This needs to be false to enable NAT functionality from the instance, or # true otherwise. For more info check the below: # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html#cfn-ec2-instance-sourcedestcheck SourceDestCheck=False if single_instance_config.is_nat else True, Tags=tags, DependsOn=single_instance_config.instance_dependencies, UserData=Base64(userdata))) if single_instance_config.is_nat: metric = 'CPUUtilization' self.sns_topic.add_alarm( description='Alarms when {0} metric {1} reaches {2}'.format( self.single.title, metric, '60'), metric=metric, namespace='AWS/EC2', threshold='60', instance=self.single) if single_instance_config.iam_instance_profile_arn: self.single.IamInstanceProfile = single_instance_config.iam_instance_profile_arn.split( '/')[1] if self.single.SourceDestCheck == 'true': # Give the instance an Elastic IP Address self.eip_address = self.template.add_resource( ec2.EIP(self.single.title + 'EIP', DependsOn=single_instance_config.instance_dependencies, Domain='vpc', InstanceId=Ref(self.single))) if single_instance_config.public_hosted_zone_name: # Create a Route53 Record Set for the instances Elastic IP address. self.si_r53 = self.template.add_resource( route53.RecordSetType( self.single.title + 'R53', HostedZoneName=single_instance_config. public_hosted_zone_name, Comment='DNS Record for {0}'.format(self.single.title), Name=Join('', [ Ref('AWS::StackName'), '-', self.single.title, '.', single_instance_config.public_hosted_zone_name ]), ResourceRecords=[Ref(self.eip_address)], Type='A', TTL='300', DependsOn=single_instance_config.instance_dependencies) ) # Create an output for the Record Set that has been created. self.template.add_output( Output(self.single.title, Description='URL of the jump host {0}'.format( self.single.title), Value=self.si_r53.Name)) else: self.template.add_output( Output(self.single.title, Description='Public IP of the jump host {0}'.format( self.single.title), Value=Ref(self.eip_address)))
HealthCheck=elb.HealthCheck( HealthyThreshold="3", Interval="5", Target="HTTP:80/", Timeout="2", UnhealthyThreshold="5", ), )) ELBcname = t.add_resource( route53.RecordSetType( "ELBcname", DependsOn=["InternetGateway", "GatewayToInternet"], # HostedZoneName=Join("", [Ref(Domain), "."]), HostedZoneId="ZTUOGS2NQKC92", Comment="CNAME to Web ELB", Name=Join("", [Ref(Project), ".showroom.", Ref(Domain)]), Type="CNAME", TTL="900", ResourceRecords=[GetAtt(ElasticLoadBalancer, "DNSName")])) # OUTPUTS VPCID = t.add_output( Output( "VPCID", Description="VPC Info.", Value=Join("", [Ref(VPC), " (", Ref(VpcCidr), ")"]), ))
[Ref("PublicSecurityGroup")], False ) InternalGatewayElb = create_load_balancer(t, "IGateway", 80, "/health-check", [Ref("PublicSecurityGroup")] ) InternalMembraneDNSRecord = t.add_resource(route53.RecordSetType( "InternalMembraneDNSRecord", HostedZoneName=Join("", cell_domain() + ["."]), Comment="CNAME redirect to internal membrane elb", Name=Join("", ["*", "."] + cell_domain()), Type="CNAME", TTL="900", ResourceRecords=[GetAtt("IGatewayElb", "DNSName")], DependsOn=["IGatewayElb", "HostedZone"] )) WaitHandle = t.add_resource(cfn.WaitConditionHandle("WaitHandle",)) def create_cellos_substack(t, name=None, role=None, cell_modules=None, tags=[], security_groups=[], load_balancers=[], instance_profile=None, instance_type=None, subnet=Ref(private_subnet), associate_public_ip=False): params = { "Role": role, "Tags": tags,
def setup_vpn(config, template): stack = config['stack'] region = config['region'] vpc_name = config['vpc'] public_subnets = [] private_subnets = [] customer_gateways = [] nat_ec2_instances = [] if region == None: print_err('%(stack)s: missing region\n' % locals()) sys.exit(1) vpcs_file = read_yaml_file('configuration/vpcs.yaml') vpcs = vpcs_file['vpcs'] connections = vpcs_file['connections'] eips = read_yaml_file('configuration/eips.yaml') # NOTE: we look for the base VPC in 'vpcs' and in eips # EIP's are allocated per VPC, since it's easier to manage if vpc_name not in vpcs: print_err('%(vpc_name)s: not found in vpcs\n' % locals()) sys.exit(1) if vpc_name not in eips: print_err( '%(stack)s: not found in eips; execute "scripts/manage-eips"\n' % locals()) sys.exit(1) vpc_id = get_vpc_id(vpc_name, region) incoming_connections = map( lambda x: x.keys()[0] if isinstance(x, dict) else x, list( itertools.chain.from_iterable( x['from'] for x in connections.values() if 'to' in x and vpc_name in x['to']))) outgoing_connections = map( lambda x: x.keys()[0] if isinstance(x, dict) else x, list( itertools.chain.from_iterable( x['to'] for x in connections.values() if 'from' in x and vpc_name in x['from']))) # if we expect incoming VPN connections then setup a VPN gateway if incoming_connections: vpn_gateway = template.add_resource( ec2.VPNGateway( 'VpnGateway', Type='ipsec.1', Tags=Tags( Name=stack, VPC=vpc_name, ), )) vpn_gateway_attachment = template.add_resource( ec2.VPCGatewayAttachment( 'VpcGatewayAttachment', VpcId=vpc_id, VpnGatewayId=Ref(vpn_gateway), )) vpn_gateway_route_propegation = template.add_resource( ec2.VPNGatewayRoutePropagation( 'VpnGatewayRoutePropagation', RouteTableIds=get_route_table_ids(vpc_id, region), VpnGatewayId=Ref(vpn_gateway), DependsOn=Name(vpn_gateway_attachment), )) for index, connection_from in enumerate(incoming_connections, 1): if connection_from not in vpcs: print_err( '%(stack)s: vpn from "%(connection_from)s" not found in vpcs\n' % locals()) sys.exit(1) if connection_from not in eips: print_err( '%(stack)s: vpn from "%(connection_from)s" not found in eips\n' % locals()) sys.exit(1) alphanumeric_id = ''.join( [y.title() for y in connection_from.split('-')]) customer_gateway = template.add_resource( ec2.CustomerGateway( alphanumeric_id + 'CGW', BgpAsn=vpcs[connection_from]['bgp_asn'], IpAddress=eips[connection_from]['public_ip'], Type='ipsec.1', Tags=Tags( Name='%(connection_from)s to %(stack)s' % locals(), VPC=vpc_name, ), )) vpn_connection = template.add_resource( ec2.VPNConnection( alphanumeric_id + 'VPNConnection', # We want this to always be 'False', for BGP StaticRoutesOnly=config['static_routing'], Type='ipsec.1', VpnGatewayId=Ref(vpn_gateway), CustomerGatewayId=Ref(customer_gateway), Tags=Tags( Name='%s CGW: IP %s' % (connection_from, eips[connection_from]['public_ip']), # The Tag 'RemoteVPC' is queried by # configuration process on the remote VPC's NAT # instance to identify the Virtual Connection they # should connect to. # It refers to the VPC stack name, not the WAN stack name RemoteVPC=connection_from, RemoteIp=eips[connection_from]['public_ip'], VPC=vpc_name, ), )) # Add static routes to the subnets behind each incoming VPN connection # NOTE: Can't be used when StaticRoutesOnly is False (which is required # when using BGP) if config['static_routing']: vpn_connection_static_route = template.add_resource( ec2.VPNConnectionRoute( '%(connection_from)s Static Route' % locals(), VpnConnectionId=Ref(vpn_connection), DestinationCidrBlock=vpcs[connection_from]['cidr'], )) customer_gateways.append(customer_gateway) else: vpn_gateway = None if outgoing_connections: if not region in config['nat']['ami_id']: print_err('AMI ID not configured for region "%(region)s"\n' % locals()) sys.exit(1) nat_sg = template.add_resource( ec2.SecurityGroup( 'NatSg', VpcId=vpc_id, GroupDescription='%(stack)s router Security Group' % locals(), SecurityGroupEgress=[ ec2.SecurityGroupRule( CidrIp='0.0.0.0/0', IpProtocol='-1', FromPort='-1', ToPort='-1', ) ], SecurityGroupIngress= # Allow all traffic from internal networks map( lambda cidr: ec2.SecurityGroupRule(CidrIp=cidr, IpProtocol='-1', FromPort='-1', ToPort='-1'), ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']) + # Allow all traffic from all other locations on our WAN map( lambda eip: ec2.SecurityGroupRule( CidrIp=eips[eip]['public_ip'] + '/32', IpProtocol='-1', FromPort='-1', ToPort='-1'), eips.keys()) + # Optional extra traffic sources map( lambda cidr: ec2.SecurityGroupRule(CidrIp=cidr, IpProtocol='-1', FromPort='-1', ToPort='-1'), config['nat']['extra_ingress_sources'] or {}), Tags=Tags(Name='%(stack)s router' % locals(), ), )) if 'openvpn_server' in config and config['openvpn_server']: nat_sg.SecurityGroupIngress.append( ec2.SecurityGroupRule( CidrIp='0.0.0.0/0', IpProtocol='udp', FromPort='1194', ToPort='1194', )) if 'external_tld' in config: template.add_resource( route53.RecordSetType( 'OpenVpnDnsRecord', Comment='%(stack)s OpenVPN server' % locals(), HostedZoneName=config['external_tld'] + '.', Name='%s.%s.' % (vpc_name, config['external_tld']), ResourceRecords=[eips[vpc_name]['public_ip']], TTL='900', Type='A')) assume_role_policy_statement = awacs.aws.Policy(Statement=[ awacs.aws.Statement( Effect=awacs.aws.Allow, Principal=awacs.aws.Principal(principal='Service', resources=['ec2.amazonaws.com']), Action=[awacs.sts.AssumeRole], ) ]) root_role = template.add_resource( iam.Role( 'RootRole', AssumeRolePolicyDocument=assume_role_policy_statement, Path='/', )) root_role_policy = template.add_resource( iam.PolicyType( 'RootRolePolicy', PolicyName='AllowAllPolicy', PolicyDocument={ 'Version': '2012-10-17', 'Statement': [{ 'Action': '*', 'Effect': 'Allow', 'Resource': '*', }] }, Roles=[Ref(root_role)], )) root_instance_profile = template.add_resource( iam.InstanceProfile( 'RootInstanceProfile', Path='/', Roles=[Ref(root_role)], )) for index, egress_config in enumerate(config['nat']['sg_egress_rules'], 1): template.add_resource( ec2.SecurityGroupEgress( 'NatSgEgressRule%d' % index, ToPort=egress_config['port'], FromPort=egress_config['port'], IpProtocol=egress_config['protocol'], CidrIp=egress_config['cidr'], GroupId=Ref(nat_sg), )) launch_configuration = template.add_resource( autoscaling.LaunchConfiguration( 'Ec2NatLaunchConfiguration', AssociatePublicIpAddress=True, SecurityGroups=[Ref(nat_sg)], IamInstanceProfile=Ref(root_instance_profile), ImageId=config['nat']['ami_id'][region], KeyName=config['nat']['key_name'], InstanceType=config['nat']['instance_type'], UserData=build_user_data(stack), )) AutoScalingGroup = template.add_resource( autoscaling.AutoScalingGroup( 'AutoScalingGroup', VPCZoneIdentifier=get_public_subnet_ids(vpc_id, region), TerminationPolicies=['ClosestToNextInstanceHour'], MinSize=1, MaxSize=2, ##### # TODO: Have to find a way for VyOS to send the signal without # having access to cfn-signal script (old python version) # That's also the reason we allow one instance - since ha-nat # can't send the signal #### # CreationPolicy=policies.CreationPolicy( # ResourceSignal=policies.ResourceSignal( # Count=2, # Timeout='PT10M', # ), # ), LaunchConfigurationName=Ref(launch_configuration), HealthCheckType='EC2', UpdatePolicy=policies.UpdatePolicy( AutoScalingRollingUpdate=policies.AutoScalingRollingUpdate( MaxBatchSize=1, MinInstancesInService=1, PauseTime='PT2M', # TODO: switch to 'True' when we teach VyOS to send signal WaitOnResourceSignals=False, )), Tags=[ autoscaling.Tag('Name', stack + ' router', True), autoscaling.Tag('VPC', vpc_name, True), # Just have to be unique for this provisioning run, could # be any unique string autoscaling.Tag( 'Version', datetime.datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S.%f'), True), ], ))
def set_up_stack(self): """Sets up the stack""" if not self.INPUTS or not self.STACK_NAME_PREFIX or not self.HEALTH_ENDPOINT: raise MKInputError( 'Must define INPUTS, STACK_NAME_PREFIX, and HEALTH_ENDPOINT') super(AppServerStack, self).set_up_stack() tags = self.get_input('Tags').copy() self.add_description('{} App Server Stack for Cac'.format( self.STACK_NAME_PREFIX)) assert isinstance(tags, dict), 'tags must be a dictionary' self.availability_zones = get_availability_zones() tags.update({'StackType': 'AppServer'}) self.default_tags = tags self.app_server_instance_type_parameter = self.add_parameter( Parameter( 'AppServerInstanceType', Type='String', Default='t2.medium', Description='NAT EC2 instance type', AllowedValues=EC2_INSTANCE_TYPES, ConstraintDescription='must be a valid EC2 instance type.'), source='AppServerInstanceType') self.param_app_server_iam_profile = self.add_parameter( Parameter('AppServerIAMProfile', Type='String', Description='IAM Profile for instances'), source='AppServerIAMProfile') self.app_server_ami = self.add_parameter(Parameter( 'AppServerAMI', Type='String', Description='{} Server EC2 AMI'.format(self.STACK_NAME_PREFIX)), source='AppServerAMI') self.keyname_parameter = self.add_parameter(Parameter( 'KeyName', Type='String', Default='cac', Description='Name of an existing EC2 key pair'), source='KeyName') self.param_color = self.add_parameter(Parameter( 'StackColor', Type='String', Description='Stack color', AllowedValues=['Blue', 'Green', 'Orange']), source='StackColor') self.param_stacktype = self.add_parameter(Parameter( 'StackType', Type='String', Description='Stack type', AllowedValues=['Development', 'Staging', 'Production']), source='StackType') self.param_public_hosted_zone_name = self.add_parameter( Parameter('PublicHostedZoneName', Type='String', Description='Public hosted zone name'), source='PublicHostedZoneName') self.param_vpc = self.add_parameter(Parameter( 'VpcId', Type='String', Description='Name of an existing VPC'), source='VpcId') self.param_notification_arn = self.add_parameter( Parameter( 'GlobalNotificationsARN', Type='String', Description='Physical resource ID on an AWS::SNS::Topic for ' 'notifications'), source='GlobalNotificationsARN') self.param_ssl_certificate_arn = self.add_parameter( Parameter('SSLCertificateARN', Type='String', Description= 'Physical resource ID on an AWS::IAM::ServerCertificate ' 'for the application server load balancer'), source='SSLCertificateARN') self.param_public_subnets = self.add_parameter( Parameter('PublicSubnets', Type='CommaDelimitedList', Description='A list of public subnets'), source='AppServerPublicSubnets') self.param_private_subnets = self.add_parameter( Parameter('PrivateSubnets', Type='CommaDelimitedList', Description='A list of private subnets'), source='AppServerPrivateSubnets') self.param_bastion_security_group = self.add_parameter( Parameter('BastionSecurityGroup', Type='String', Description='The ID of the bastion security group'), source='BastionSecurityGroup') self.param_database_security_group = self.add_parameter( Parameter('DatabaseSecurityGroup', Type='String', Description='The ID of the database security group'), source='DatabaseSecurityGroup') self.param_nat_security_group = self.add_parameter( Parameter('NATSecurityGroup', Type='String', Description='The ID of the NAT security group'), source='NATSecurityGroup') self.param_min_size = self.add_parameter(Parameter( 'ASGMinSize', Type='Number', Default='1', Description='Min size of ASG'), source='ASGMinSize') self.param_max_size = self.add_parameter(Parameter( 'ASGMaxSize', Type='Number', Default='1', Description='Max size of ASG'), source='ASGMaxSize') self.param_desired_capacity = self.add_parameter( Parameter('ASGDesiredCapacity', Type='Number', Default='1', Description='Desired capacity of ASG'), source='ASGDesiredCapacity') # # Security Group # app_server_load_balancer_security_group = self.add_resource( ec2.SecurityGroup( 'sgAppServerLoadBalancer', GroupDescription= 'Enables access to app servers via a load balancer', VpcId=Ref(self.param_vpc), SecurityGroupIngress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=ALLOW_ALL_CIDR, FromPort=p, ToPort=p) for p in [80, 443] ], Tags=Tags(Name='sgAppServerLoadBalancer', Color=Ref(self.param_color)))) app_server_security_group = self.add_resource( ec2.SecurityGroup( 'sgAppServer', GroupDescription='Enables access to App Servers', VpcId=Ref(self.param_vpc), SecurityGroupIngress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=VPC_CIDR, FromPort=p, ToPort=p) for p in [22, 80, 443] ] + [ ec2.SecurityGroupRule(IpProtocol='tcp', SourceSecurityGroupId=Ref(sg), FromPort=80, ToPort=80) for sg in [app_server_load_balancer_security_group] ] + [ ec2.SecurityGroupRule(IpProtocol='tcp', SourceSecurityGroupId=Ref(sg), FromPort=443, ToPort=443) for sg in [app_server_load_balancer_security_group] ], SecurityGroupEgress=[ ec2.SecurityGroupRule(IpProtocol='tcp', CidrIp=ALLOW_ALL_CIDR, FromPort=p, ToPort=p) for p in [80, 443, PAPERTRAIL_PORT] ], Tags=Tags(Name='sgAppServer', Color=Ref(self.param_color)))) # ELB to App Server self.add_resource( ec2.SecurityGroupEgress( 'sgEgressELBtoAppHTTP', GroupId=Ref(app_server_load_balancer_security_group), DestinationSecurityGroupId=Ref(app_server_security_group), IpProtocol='tcp', FromPort=80, ToPort=80)) self.add_resource( ec2.SecurityGroupEgress( 'sgEgressELBtoAppHTTPS', GroupId=Ref(app_server_load_balancer_security_group), DestinationSecurityGroupId=Ref(app_server_security_group), IpProtocol='tcp', FromPort=443, ToPort=443)) # Bastion to App Server, app server to db, app server to inet rules = [(self.param_bastion_security_group, app_server_security_group, [80, 443, 22]), (app_server_security_group, self.param_database_security_group, [POSTGRES]), (app_server_security_group, self.param_nat_security_group, [80, 443, 22, 587, PAPERTRAIL_PORT])] for num, (srcsg, destsg, ports) in enumerate(rules): for port in ports: self.add_resource( ec2.SecurityGroupEgress( 'sgEgress{}p{}'.format(num, port), GroupId=Ref(srcsg), DestinationSecurityGroupId=Ref(destsg), IpProtocol='tcp', FromPort=port, ToPort=port)) self.add_resource( ec2.SecurityGroupIngress('sgIngress{}p{}'.format( num, port), GroupId=Ref(destsg), SourceSecurityGroupId=Ref(srcsg), IpProtocol='tcp', FromPort=port, ToPort=port)) # # ELB # app_server_load_balancer = self.add_resource( elb.LoadBalancer( 'elbAppServer', ConnectionDrainingPolicy=elb.ConnectionDrainingPolicy( Enabled=True, Timeout=300), CrossZone=True, SecurityGroups=[Ref(app_server_load_balancer_security_group)], Listeners=[ elb.Listener(LoadBalancerPort='80', Protocol='HTTP', InstancePort='80', InstanceProtocol='HTTP'), elb.Listener(LoadBalancerPort='443', Protocol='HTTPS', InstancePort='443', InstanceProtocol='HTTP', SSLCertificateId=Ref( self.param_ssl_certificate_arn)) ], HealthCheck=elb.HealthCheck( Target=self.HEALTH_ENDPOINT, HealthyThreshold='3', UnhealthyThreshold='2', Interval='30', Timeout='5', ), Subnets=Ref(self.param_public_subnets), Tags=Tags(Name='elbAppServer', Color=Ref(self.param_color)))) self.add_resource( cw.Alarm('alarmAppServerBackend4xx', AlarmActions=[Ref(self.param_notification_arn)], Statistic='Sum', Period=300, Threshold='5', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_4XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_load_balancer)) ])) self.add_resource( cw.Alarm('alarmAppServerBackend5xx', AlarmActions=[Ref(self.param_notification_arn)], Statistic='Sum', Period=60, Threshold='0', EvaluationPeriods=1, ComparisonOperator='GreaterThanThreshold', MetricName='HTTPCode_Backend_5XX', Namespace='AWS/ELB', Dimensions=[ cw.MetricDimension( 'metricLoadBalancerName', Name='LoadBalancerName', Value=Ref(app_server_load_balancer)) ])) # # ASG # app_server_launch_config = self.add_resource( asg.LaunchConfiguration( 'lcAppServer', ImageId=Ref(self.app_server_ami), IamInstanceProfile=Ref(self.param_app_server_iam_profile), InstanceType=Ref(self.app_server_instance_type_parameter), KeyName=Ref(self.keyname_parameter), SecurityGroups=[Ref(app_server_security_group)])) autoscaling_group = self.add_resource( asg.AutoScalingGroup( 'asgAppServer', AvailabilityZones=self.get_input( 'AppServerAvailabilityZones').split(','), Cooldown=300, DesiredCapacity=Ref(self.param_desired_capacity), HealthCheckGracePeriod=600, HealthCheckType='ELB', LaunchConfigurationName=Ref(app_server_launch_config), LoadBalancerNames=[Ref(app_server_load_balancer)], MaxSize=Ref(self.param_max_size), MinSize=Ref(self.param_min_size), NotificationConfiguration=asg.NotificationConfiguration( TopicARN=Ref(self.param_notification_arn), NotificationTypes=[ asg.EC2_INSTANCE_LAUNCH, asg.EC2_INSTANCE_LAUNCH_ERROR, asg.EC2_INSTANCE_TERMINATE, asg.EC2_INSTANCE_TERMINATE_ERROR ]), VPCZoneIdentifier=Ref(self.param_private_subnets), Tags=[ asg.Tag('Name', '{}Server'.format(self.STACK_NAME_PREFIX), True), asg.Tag('Color', Ref(self.param_color), True) ])) # autoscaling policies autoscaling_policy_add = self.add_resource( asg.ScalingPolicy('scalingPolicyAddAppServer', AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(autoscaling_group), Cooldown=600, ScalingAdjustment='1')) autoscaling_policy_remove = self.add_resource( asg.ScalingPolicy('scalingPolicyRemoveAppServer', AdjustmentType='ChangeInCapacity', AutoScalingGroupName=Ref(autoscaling_group), Cooldown=600, ScalingAdjustment='-1')) if self.STACK_NAME_PREFIX == 'Otp': # trigger scale down if CPU avg usage < 10% for 3 consecutive 5 min periods self.add_resource( cw.Alarm('alarmAppServerLowCPU', AlarmActions=[Ref(autoscaling_policy_remove)], Statistic='Average', Period=300, Threshold='10', EvaluationPeriods=3, ComparisonOperator='LessThanThreshold', MetricName='CPUUtilization', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) # trigger scale up if CPU avg usage >= 30% for a 5 min period self.add_resource( cw.Alarm('alarmAppServerHighCPU', AlarmActions=[ Ref(self.param_notification_arn), Ref(autoscaling_policy_add) ], Statistic='Average', Period=300, Threshold='30', EvaluationPeriods=1, ComparisonOperator='GreaterThanOrEqualToThreshold', MetricName='CPUUtilization', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) else: # scale web servers based on network usage self.add_resource( cw.Alarm('alarmAppServerLowNetworkUsage', AlarmActions=[Ref(autoscaling_policy_remove)], Statistic='Average', Period=300, Threshold='500000', EvaluationPeriods=3, ComparisonOperator='LessThanThreshold', MetricName='NetworkOut', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) self.add_resource( cw.Alarm('alarmAppServerHighNetworkUsage', AlarmActions=[ Ref(self.param_notification_arn), Ref(autoscaling_policy_add) ], Statistic='Average', Period=300, Threshold='10000000', EvaluationPeriods=1, ComparisonOperator='GreaterThanOrEqualToThreshold', MetricName='NetworkOut', Namespace='AWS/EC2', Dimensions=[ cw.MetricDimension('metricAutoScalingGroupName', Name='AutoScalingGroupName', Value=Ref(autoscaling_group)) ])) # # DNS name # self.create_resource( route53.RecordSetType( 'dnsName', Name=Join('.', [ Ref(self.param_color), Ref(self.param_stacktype), self.STACK_NAME_PREFIX, Ref(self.param_public_hosted_zone_name) ]), Type='A', AliasTarget=route53.AliasTarget( GetAtt(app_server_load_balancer, 'CanonicalHostedZoneNameID'), GetAtt(app_server_load_balancer, 'DNSName')), HostedZoneName=Ref(self.param_public_hosted_zone_name))) self.add_output([ Output('{}ServerLoadBalancerEndpoint'.format( self.STACK_NAME_PREFIX), Description='Application server endpoint', Value=GetAtt(app_server_load_balancer, 'DNSName')), Output('{}ServerLoadBalancerHostedZoneNameID'.format( self.STACK_NAME_PREFIX), Description='ID of canonical hosted zone name for ELB', Value=GetAtt(app_server_load_balancer, 'CanonicalHostedZoneNameID')) ])
def __init__(self, tags=dict()): super(RDSFactory, self).__init__() self.tags = tags # Largely copied from # https://github.com/cloudtools/troposphere/blob/master/examples/RDS_VPC.py # Each parameter is followed by the resources which depend on it. # VPC and security groups vpcid = Parameter( 'VpcId', Type='String', Description='Id of existing VPC' ) private_hosted_zone_id = Parameter( 'PrivateHostedZoneId', Type='String', Description='Private hosted zone id' ) db_security_group = ec2.SecurityGroup( 'sgDatabase', GroupDescription='Security group for RDS DB Instance.', VpcId=Ref(vpcid), Tags=Tags(Name='Database', **self.tags) ) # Subnets subnets = Parameter( 'AppServerSubnets', Type='CommaDelimitedList', Description='List of SubnetIds spanning at least two AZs in VPC' ) subnet_group = rds.DBSubnetGroup( 'CacDbSubnetGroup', DBSubnetGroupDescription='Subnets available for Cac RDS instance', SubnetIds=Ref(subnets), Tags=Tags(Name='RDSSubnetGroup', **self.tags) ) # Database db_name = Parameter( 'DbName', Description='Name of the database to be created', Type='String', MinLength='5', MaxLength='63', AllowedPattern='[a-zA-Z_][a-zA-Z0-9_]*', ConstraintDescription='Name must begin with a letter and contain only alphanumerics' ) db_user = Parameter( 'DbUser', NoEcho=True, Description='Database admin user account', Type='String', MinLength='5', MaxLength='16', AllowedPattern='[a-zA-Z][a-zA-Z0-9]*', ConstraintDescription='Name must begin with a letter and contain only alphanumerics' ) db_password = Parameter( 'DbPassword', NoEcho=True, Description='Database admin account password', Type='String', MinLength='8', ) db_instance_class = Parameter( 'DbInstanceClass', Default='db.m3.medium', Description='Database instance class', Type='String', AllowedValues=RDS_INSTANCE_TYPES ) db_storage = Parameter( 'DbStorage', Description='Available database storage (GB)', Default='100', Type='Number', MaxValue='1024', ConstraintDescription='Storage space must be less than 1024GB', ) db_dns_name = Parameter( 'DbDNSName', Type='String', Description='Private DNS name for database' ) database = rds.DBInstance( 'CacDb', DBName=Ref(db_name), AllocatedStorage=Ref(db_storage), DBInstanceClass=Ref(db_instance_class), Engine='postgres', EngineVersion='9.3', MasterUsername=Ref(db_user), MasterUserPassword=Ref(db_password), DBSubnetGroupName=Ref(subnet_group), VPCSecurityGroups=[Ref(db_security_group)], MultiAZ=True, Tags=Tags(Name='CacDB', **self.tags) ) db_dns_record = route53.RecordSetType( 'rsDatabase', Name=Ref(db_dns_name), ResourceRecords=[GetAtt('CacDb', 'Endpoint.Address')], TTL=600, Type='CNAME', HostedZoneId=Ref(private_hosted_zone_id), ) # Outputs rds_endpoint = Output( 'CacDbEndpoint', Description='Endpoint to which Postgres clients should connect', Value=GetAtt('CacDb', 'Endpoint.Address') ) database_name = Output( 'CacDbName', Description='Name of database created on Cac RDS instance', Value=Ref(db_name) ) db_sg = Output( 'DatabaseSecurityGroup', Description='Security Group of Database', Value=GetAtt('sgDatabase', 'GroupId') ) self.parameters = [vpcid, private_hosted_zone_id, subnets, db_name, db_user, db_password, db_instance_class, db_storage, db_dns_name] self.resources = [db_security_group, subnet_group, database, db_dns_record] self.outputs = [rds_endpoint, database_name, db_sg]
SslSupportMethod='sni-only', ), ), Tags=GetAtt(cloudformation_tags, 'TagList'), )) hosted_zone_map = "HostedZoneMap" template.add_mapping(hosted_zone_map, cfnutils.mappings.r53_hosted_zone_id()) template.add_resource( route53.RecordSetType( "DomainA", AliasTarget=route53.AliasTarget( DNSName=GetAtt(example_distribution, 'DomainName'), HostedZoneId=FindInMap(hosted_zone_map, Ref(AWS_REGION), 'CloudFront'), ), Comment=Sub('DNS for ${AWS::StackName}'), HostedZoneName=Join('', [Ref(param_hosted_zone_name), '.']), Name=domain_name, Type='A', )) template.add_resource( route53.RecordSetType( "DomainAAAA", AliasTarget=route53.AliasTarget( DNSName=GetAtt(example_distribution, 'DomainName'), HostedZoneId=FindInMap(hosted_zone_map, Ref(AWS_REGION), 'CloudFront'), ), Comment=Sub('DNS for ${AWS::StackName}'), HostedZoneName=Join('', [Ref(param_hosted_zone_name), '.']),
Stage='Prod', # Default name of Serverless generated Stage Condition=use_cert_cond, )) hosted_zone_map = "HostedZoneMap" template.add_mapping(hosted_zone_map, cfnutils.mappings.r53_hosted_zone_id()) template.add_resource( route53.RecordSetType( "DomainA", AliasTarget=route53.AliasTarget( DNSName=GetAtt(api_domain, 'DistributionDomainName'), HostedZoneId=FindInMap(hosted_zone_map, Ref(AWS_REGION), 'CloudFront'), ), Comment=Sub('Default DNS for ${AWS::StackName} api'), HostedZoneName=Join('', [Ref(param_hosted_zone_name), '.']), Name=Join( '.', [Ref(param_label), Ref(param_hosted_zone_name)]), Type='A', Condition=use_cert_cond, )) template.add_resource( route53.RecordSetType( "DomainAAAA", AliasTarget=route53.AliasTarget( DNSName=GetAtt(api_domain, 'DistributionDomainName'), HostedZoneId=FindInMap(hosted_zone_map, Ref(AWS_REGION), 'CloudFront'),
param_target = t.add_parameter( Parameter( 'Target', Description='Alias/cname target', Type='String', )) # # Resource # record = t.add_resource( route53.RecordSetType( 'Record', HostedZoneName=Sub('${HostedZoneName}.'), Name=Join('.', [Ref(param_domain_name), Ref(param_hosted_domain)]), Type=Ref(param_record_type), TTL=Ref(param_ttl), ResourceRecords=[Ref(param_target)])) # # Output # t.add_output([ Output( 'DnsName', Description='DNS name', Value=Ref(record), Export=Export(Sub('${AWS::StackName}-DnsName')), ), ])