def create_dnsrecords(): return route53.RecordSetGroup( 'dns', HostedZoneName=Sub('${domain}.'), RecordSets=[ route53.RecordSet( 'dnsInstance', Name=_subdomain_for_instance(), ResourceRecords=[GetAtt('devserver', 'PublicIp')], Type='A', TTL=300, ), route53.RecordSet( 'dnsTlsApplication', Name=_subdomain_for_application(), Type='A', AliasTarget=route53.AliasTarget( DNSName=GetAtt('tlsFrontend', 'DNSName'), HostedZoneId=GetAtt('tlsFrontend', 'CanonicalHostedZoneID'), ), ), route53.RecordSet( 'dnsTlsJenkins', Name=_subdomain_for_jenkins(), Type='A', AliasTarget=route53.AliasTarget( DNSName=GetAtt('tlsFrontend', 'DNSName'), HostedZoneId=GetAtt('tlsFrontend', 'CanonicalHostedZoneID'), ), ), ], )
def create_dns_records(self, rds_database, elasticache_group): self.add_resource( r53.RecordSetGroup( 'dnsPrivateRecords', HostedZoneId=Ref(self.private_hosted_zone_id), RecordSets=[ r53.RecordSet('dnsDatabaseServer', Name=Join('', [ 'database.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='CNAME', TTL='10', ResourceRecords=[ GetAtt(rds_database, 'Endpoint.Address') ]), r53.RecordSet('dnsCacheServer', Name=Join('', [ 'cache.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='CNAME', TTL='10', ResourceRecords=[ GetAtt(elasticache_group, 'PrimaryEndPoint.Address') ]) ]))
def create_dns_records(self, bastion_host, rds_database, elasticache_group): self.add_resource( r53.RecordSetGroup( 'dnsPublicRecords', HostedZoneName=Join('', [Ref(self.public_hosted_zone_name), '.']), RecordSets=[ r53.RecordSet( 'dnsMonitoringServer', Name=Join('', [ 'monitoring.', Ref(self.public_hosted_zone_name), '.' ]), Type='A', TTL='300', ResourceRecords=[GetAtt(bastion_host, 'PublicIp')]) ])) self.add_resource( r53.RecordSetGroup( 'dnsPrivateRecords', HostedZoneId=Ref(self.private_hosted_zone_id), RecordSets=[ r53.RecordSet( 'dnsBastionHost', Name=Join('', [ 'monitoring.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='A', TTL='10', ResourceRecords=[GetAtt(bastion_host, 'PrivateIp')]), r53.RecordSet('dnsDatabaseServer', Name=Join('', [ 'database.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='CNAME', TTL='10', ResourceRecords=[ GetAtt(rds_database, 'Endpoint.Address') ]), r53.RecordSet('dnsCacheServer', Name=Join('', [ 'cache.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='CNAME', TTL='10', ResourceRecords=[ GetAtt(elasticache_group, 'PrimaryEndPoint.Address') ]) ]))
def handle(self, chain_context): template = chain_context.template template.add_resource(route53.RecordSetGroup( "Route53Records", RecordSets=[ route53.RecordSet( DNS_NAME % self.name, Weight=1, SetIdentifier="original", AliasTarget=route53.AliasTarget( HostedZoneId=self.hosted_zone_id, DNSName=self.target, EvaluateTargetHealth=False, ), Name=Join("", [ self.dns_name, ".", self.base_domain, "." ]), Type="A", ) ], HostedZoneName=Join("", [self.base_domain, "."]) ))
def create_r53_record(self, hosted_zone_name): """ Function to create r53 recourdset to associate with ELB :param hosted_zone_name: R53 hosted zone to create record in """ if self.elb_config.public_unit: name = Join('', [Ref('AWS::StackName'), '-', self.title, '.', hosted_zone_name]) else: name = Join('', [self.title, '.', hosted_zone_name]) self.elb_r53 = self.template.add_resource(route53.RecordSetGroup( self.title + 'R53', RecordSets=[route53.RecordSet( Name=name, AliasTarget=route53.AliasTarget(dnsname=GetAtt(self.trop_elb, 'DNSName'), hostedzoneid=GetAtt(self.trop_elb, 'CanonicalHostedZoneNameID')), Type='A')])) if not self.elb_config.public_unit: self.elb_r53.HostedZoneId = self.network_config.private_hosted_zone_id else: self.elb_r53.HostedZoneName = hosted_zone_name self.template.add_output(Output( self.trop_elb.title, Description='URL of the {0} ELB'.format(self.title), Value=Join('', ['http://', self.elb_r53.RecordSets[0].Name]) ))
def create_r53_record(self): """ Function to create r53 recourdset to associate with the RDS """ self.rds_r53 = self.template.add_resource( route53.RecordSetGroup( self.title + 'R53', HostedZoneId=self.network_config.private_hosted_zone_id, RecordSets=[ route53.RecordSet(Name=Join('', [ self.title, '.', self.network_config.private_hosted_zone_domain ]), ResourceRecords=[ GetAtt(self.trop_db, 'Endpoint.Address') ], TTL=300, Type='CNAME') ])) self.template.add_output( Output(self.trop_db.title + 'Endpoint', Description='Address of the {0} RDS'.format(self.title), Value=Join('', [ self.rds_r53.RecordSets[0].Name, ':', GetAtt(self.trop_db, 'Endpoint.Port') ])))
def route53_record_set(self): t = self.template LocalDNS = t.add_resource( route53.RecordSetGroup( "LocalDNS", HostedZoneName=self.vars["HostedZoneDomainName"] + ".", RecordSets=[ route53.RecordSet( Name=self.vars["FQDNInternal"] + ".", Type="A", AliasTarget=route53.AliasTarget( HostedZoneId=CLOUDFRONT_HOSTEDZONEID, DNSName=GetAtt(self.SiteCFDistribution, "DomainName"), ), ) ], )) LocalDNS = t.add_output( Output( "LocalDNS", Description="Internal DNS domainname set in route53", Value=self.vars["FQDNInternal"], ))
def handle(self, chain_context): template = chain_context.template name = 'AlbAlias%s' % chain_context.instance_name template.add_resource( route53.RecordSetGroup( "Route53Records", RecordSets=[ route53.RecordSet( name, Weight=1, SetIdentifier="original", AliasTarget=route53.AliasTarget( HostedZoneId=self.hosted_zone_id, DNSName=self.dns_name, EvaluateTargetHealth=False, ), Name=Join("", [ Ref("namespace"), "-", Ref("env"), ".", self.base_domain, "." ]), Type="A", ) ], HostedZoneName=Join("", [self.base_domain, "."])))
def create_dns_records(self, bastion_host): private_hosted_zone = self.add_resource( r53.HostedZone('dnsPrivateHostedZone', Name=Join( '', [Ref(self.private_hosted_zone_name), '.']), VPCs=[ r53.HostedZoneVPCs(VPCId=Ref(self.vpc), VPCRegion=self.region) ])) self.add_resource( r53.RecordSetGroup( 'dnsPublicRecords', HostedZoneName=Join('', [Ref(self.public_hosted_zone_name), '.']), RecordSets=[ r53.RecordSet( 'dnsMonitoringServer', Name=Join('', [ 'monitoring.', Ref(self.public_hosted_zone_name), '.' ]), Type='A', TTL='300', ResourceRecords=[GetAtt(bastion_host, 'PublicIp')]) ])) self.add_resource( r53.RecordSetGroup( 'dnsPrivateRecords', HostedZoneId=Ref(private_hosted_zone), RecordSets=[ r53.RecordSet( 'dnsBastionHost', Name=Join('', [ 'monitoring.service.', Ref(self.private_hosted_zone_name), '.' ]), Type='A', TTL='10', ResourceRecords=[GetAtt(bastion_host, 'PrivateIp')]) ])) return private_hosted_zone
def create_dns_records(self, tile_server_lb): self.add_condition('BlueCondition', Equals('Blue', Ref(self.color))) self.add_condition('GreenCondition', Equals('Green', Ref(self.color))) self.add_resource(r53.RecordSetGroup( 'dnsPublicRecordsBlue', Condition='BlueCondition', HostedZoneName=Join('', [Ref(self.public_hosted_zone_name), '.']), RecordSets=[ r53.RecordSet( 'dnsTileServersBlue', AliasTarget=r53.AliasTarget( GetAtt(tile_server_lb, 'CanonicalHostedZoneNameID'), GetAtt(tile_server_lb, 'DNSName'), True ), Name=Join('', ['blue-tiles.', Ref(self.public_hosted_zone_name), '.']), Type='A' ) ] )) self.add_resource(r53.RecordSetGroup( 'dnsPublicRecordsGreen', Condition='GreenCondition', HostedZoneName=Join('', [Ref(self.public_hosted_zone_name), '.']), RecordSets=[ r53.RecordSet( 'dnsTileServersGreen', AliasTarget=r53.AliasTarget( GetAtt(tile_server_lb, 'CanonicalHostedZoneNameID'), GetAtt(tile_server_lb, 'DNSName'), True ), Name=Join('', ['green-tiles.', Ref(self.public_hosted_zone_name), '.']), Type='A' ) ] ))
def add_to_template(self, t): dns_param = ensure_param(t, self.value.output_dns(), 'String') zone_id = "Z2FDTNDATAQYW2" r = route53.RecordSet( '{}CFRecord'.format(self._safe_dns_name(self.name)), Name="{}{}.".format(self.name, self.stack.domain_name), Type="A", AliasTarget=route53.AliasTarget(HostedZoneId=zone_id, DNSName=Ref(dns_param))) return r
def add_to_template(self, t): dns_param = ensure_param(t, self.value.output_endpoint(), 'String') r = route53.RecordSet('{}RDSRecord'.format( self._safe_dns_name(self.name)), Name="{}{}.".format(self.name, self.stack.domain_name), Type="CNAME", TTL=self.ttl, ResourceRecords=[Ref(dns_param)]) return r
def create_s3_resources(self): s3_bucket = self.add_resource( s3.Bucket('s3TileCacheBucket', BucketName=Join( '.', ['tile-cache', Ref(self.public_hosted_zone_name)]), AccessControl=s3.PublicRead, CorsConfiguration=s3.CorsConfiguration(CorsRules=[ s3.CorsRules( AllowedOrigins=['*'], AllowedMethods=['GET'], MaxAge=3000, AllowedHeaders=['*'], ) ]))) self.add_resource( s3.BucketPolicy( 's3TileCacheBucketPolicy', Bucket=Ref(s3_bucket), PolicyDocument={ 'Statement': [{ 'Action': ['s3:GetObject'], 'Effect': 'Allow', 'Resource': { 'Fn::Join': ['', ['arn:aws:s3:::', Ref(s3_bucket), '/*']] }, 'Principal': '*' }] })) self.add_resource( r53.RecordSetGroup( 'dnsPublicRecordsCache', HostedZoneName=Join('', [Ref(self.public_hosted_zone_name), '.']), RecordSets=[ r53.RecordSet('dnsTileServersCache', AliasTarget=r53.AliasTarget( AMAZON_S3_HOSTED_ZONE_ID, AMAZON_S3_WEBSITE_DOMAIN, True, ), Name=Join('', [ 'tile-cache.', Ref(self.public_hosted_zone_name), '.' ]), Type='A') ]))
def add_to_template(self, t): """ """ zone_param = ensure_param(t, self.value.output_hosted_zone(), 'String') dns_param = ensure_param(t, self.value.output_dns_name(), 'String') r = route53.RecordSet( '{}ELBRecord'.format(self._safe_dns_name(self.name)), Name="{}{}.".format(self.name, self.stack.domain_name), Type="A", AliasTarget=route53.AliasTarget(HostedZoneId=Ref(zone_param), DNSName=Ref(dns_param))) return r
def add_to_template(self, template): param = Ref(ensure_param(template, self.value.output_eip())) record = route53.RecordSet('{}EipARecord'.format( self._safe_dns_name(self.name)), Name="{}{}.".format(self.name, self.stack.domain_name), Type=self.type, TTL=self.ttl, ResourceRecords=[param]) return record
def add_api(self): api = self.add_resource( apigatewayv2.Api( 'HttpApi', Name=StackName, Description=Join(' ', [Ref(self.domain), 'Terraform Registry']), ProtocolType='HTTP', Target=Ref(self._lambda_function), )) self.add_resource( awslambda.Permission( f'ApigatewayPermission', Principal='apigateway.amazonaws.com', Action='lambda:InvokeFunction', FunctionName=Ref(self._lambda_function), SourceArn=Join('', [ 'arn:aws:execute-api:', Region, ':', AccountId, ':', Ref(api), '/*' ]))) domain = self.add_resource( apigatewayv2.DomainName('HttpApiDomain', DomainName=Ref(self.domain), DomainNameConfigurations=[ apigatewayv2.DomainNameConfiguration( CertificateArn=Ref( self.certificate), ) ])) mapping = self.add_resource( apigatewayv2.ApiMapping('Mapping', DomainName=Ref(domain), ApiId=Ref(api), Stage='$default')) dns_record = self.add_resource( route53.RecordSetGroup( 'ApiDnsRecord', HostedZoneId=Ref(self.hosted_zone), RecordSets=[ route53.RecordSet( Name=Ref(self.domain), AliasTarget=route53.AliasTarget( DNSName=GetAtt(domain, 'RegionalDomainName'), HostedZoneId=GetAtt(domain, 'RegionalHostedZoneId')), Type='A') ]))
def add_to_template(self, template): res = self.value if not isinstance(res, list): res = [res] record = route53.RecordSet('{}ARecord'.format( self._safe_dns_name(self.name)), Name="{}{}.".format(self.name, self.stack.domain_name), Type=self.type, TTL=self.ttl, ResourceRecords=res) return record
def add_elasticache_dns_alias(self, cluster, name, engine, zone_name): if engine == 'redis': address = "RedisEndpoint.Address" if engine == 'memcached': address = "ConfigurationEndpoint.Address" return self.add_resource( route53.RecordSetGroup( name + engine + "ElastiCacheRecordSetGroup", HostedZoneName=zone_name, RecordSets=[ route53.RecordSet( Name=name + engine + '.' + zone_name, Type='CNAME', TTL='60', ResourceRecords=[GetAtt(cluster, address)]) ]))
def add_dns_alias(self, name, dns_name, zone_id, zone_name): """ Helper to attach an alias dns entry to an elb @param dns_name [string] name of the domain @param zone_id [string] hostzone id for the target @param zone_name [string] hostzone name """ return self.add_resource( route53.RecordSetGroup( name.replace(".", "") + "AliasRecordSetGroup" + zone_name.replace('.', ''), HostedZoneName=zone_name, RecordSets=[ route53.RecordSet(Name=name, Type='A', AliasTarget=route53.AliasTarget( zone_id, dns_name)) ]))
def route53_record_set(self): t = self.template t.add_resource( route53.RecordSetGroup( "RecordSetGroup", HostedZoneName=self.vars["HostedZone"] + ".", RecordSets=[ route53.RecordSet( Name=self.vars['ServiceFqdn'] + ".", Type="A", AliasTarget=route53.AliasTarget( HostedZoneId=get_elb_hosted_zone_id( self.vars['LoadBalancerArn']), DNSName=self.vars['LoadBalancerUrl'], ), ) ], )) return
def add_instance_dns_a_record(self, instance, name, zone_name, att='PrivateIp'): """ Helper to attach an alias dns entry to an elb @param instance [Instance] target instance @param name [string] name of the domain @param zone_name [string] hostzone name @param att [string] the attribute to use for binding the record """ return self.add_resource( route53.RecordSetGroup( name.replace(".", "") + "InstanceRecordSetGroup", HostedZoneName=zone_name, RecordSets=[ route53.RecordSet(Name=name + '.' + zone_name, Type='A', TTL='60', ResourceRecords=[GetAtt(instance, att)]) ]))
def add_elb_dns_alias(self, elb, name, zone_name): """ Helper to attach an alias dns entry to an elb @param elb [ELB] target elb @param name [string] name of the domain @param zone_name [string] hostzone name """ if name: name = name.lower() + '.' + zone_name else: name = zone_name.lower() return self.add_resource( route53.RecordSetGroup( name.replace('.', '') + "ELBRecordSetGroup" + zone_name.replace('.', ''), HostedZoneName=zone_name.lower(), RecordSets=[ route53.RecordSet(Name=name, Type='A', AliasTarget=route53.AliasTarget( GetAtt(elb, "CanonicalHostedZoneID"), GetAtt(elb, "DNSName"))) ]))
def add_rds_dns_alias(self, rds, name, zone_name): """ Helper to attach an alias dns entry to an elb @param instance [Instance] target instance @param name [string] name of the domain @param zone_name [string] hostzone name @param att [string] the attribute to use for binding the record """ if 'admin' in zone_name: record_name = 'rds' + name + '.' + zone_name else: record_name = name + '.rds.' + zone_name return self.add_resource( route53.RecordSetGroup( name.replace(".", "") + "RDSRecordSetGroup", HostedZoneName=zone_name, RecordSets=[ route53.RecordSet( Name=record_name, Type='CNAME', TTL='60', ResourceRecords=[GetAtt(rds, "Endpoint.Address")]) ]))
def add_api(self): rest_api = self.add_resource( apigateway.RestApi( 'Api', Description=Join(' ', [Ref(self.domain), 'Terraform Registry']), Name=StackName)) methods = self.add_registry_api(rest_api) methods += [self.add_service_discovery_api(rest_api)] self.add_resource( awslambda.Permission( f'ApigatewayPermission', Principal='apigateway.amazonaws.com', Action='lambda:InvokeFunction', FunctionName=Ref(self._lambda_function), SourceArn=Join('', [ 'arn:aws:execute-api:', Region, ':', AccountId, ':', Ref(rest_api), '/*' ]))) deployment_id = 'ApiDeployment' + ''.join( random.choice(string.ascii_letters) for _ in range(5)) deployment = self.add_resource( apigateway.Deployment(deployment_id, Description=self._build_version, RestApiId=Ref(rest_api), DependsOn=methods, DeletionPolicy=Retain)) stage = self.add_resource( apigateway.Stage('ApiStage', MethodSettings=[ apigateway.MethodSetting( HttpMethod='*', LoggingLevel='INFO', MetricsEnabled=True, ResourcePath='/*', DataTraceEnabled=True, ) ], TracingEnabled=True, StageName='prd', RestApiId=Ref(rest_api), DeploymentId=Ref(deployment), DependsOn=[deployment])) domain = self.add_resource( apigateway.DomainName( 'ApiDomain', DomainName=Ref(self.domain), CertificateArn=Ref(self.certificate), EndpointConfiguration=apigateway.EndpointConfiguration( Types=['EDGE']))) mapping = self.add_resource( apigateway.BasePathMapping('Mapping', DomainName=Ref(domain), RestApiId=Ref(rest_api), Stage='prd', DependsOn=['ApiStage'])) dns_record = self.add_resource( route53.RecordSetGroup('ApiDnsRecord', HostedZoneId=Ref(self.hosted_zone), RecordSets=[ route53.RecordSet( Name=Ref(self.domain), AliasTarget=route53.AliasTarget( DNSName=GetAtt( domain, 'DistributionDomainName'), HostedZoneId='Z2FDTNDATAQYW2'), Type='A') ]))
def configure(self): """ Returns a Pritunl template """ self.defaults = {'instance_type': 't3.large'} self.service = 'pritunl' self.set_description('Sets up Pritunl servers') self.get_default_security_groups() self.get_standard_parameters() self.get_standard_policies() _vpn_config = constants.ENVIRONMENTS[self.env]['pritunl'] _global_config = constants.ENVIRONMENTS[self.env] _bootstrap_mode = _vpn_config.get('bootstrap_mode', False) _bootstrap_ami = get_latest_ami_id( self.region, 'amzn2-ami-hvm-2.0.????????-x86_64-gp2', 'amazon') _ivy_ami = get_latest_ami_id(self.region, 'ivy-base', _global_config.get('ami_owner', 'self')) self.ami = self.add_parameter( Parameter('AMI', Type='String', Description='AMI ID for instances', Default=_bootstrap_ami if _bootstrap_mode else _ivy_ami)) _public_dns = _vpn_config['public_dns'] _vpn_name = '{}Pritunl'.format(self.env) # We want the preferred subnet only. _vpn_subnet = self.get_subnets('public', _preferred_only=True)[0] # Add our security group _vpn_security_group = self.add_resource( ec2.SecurityGroup( '{}SecurityGroup'.format(_vpn_name), VpcId=self.vpc_id, GroupDescription='Security Group for Pritunl {}'.format( _vpn_name), SecurityGroupIngress=[ { "IpProtocol": "icmp", "FromPort": "-1", "ToPort": "-1", "CidrIp": "0.0.0.0/0" }, # Ping { "IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "0.0.0.0/0" }, # HTTP { "IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "0.0.0.0/0" }, # HTTPS { "IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0" }, # SSH { "IpProtocol": "udp", "FromPort": "10000", "ToPort": "20000", "CidrIp": "0.0.0.0/0" }, # HTTPS/OVPN { "IpProtocol": "tcp", "FromPort": "27017", "ToPort": "27017", "CidrIp": constants.SUPERNET }, # mongodb master { "IpProtocol": "-1", "FromPort": "-1", "ToPort": "-1", "CidrIp": constants.SUPERNET } # Replies from local VPC ], SecurityGroupEgress=[{ "IpProtocol": "-1", "FromPort": "-1", "ToPort": "-1", "CidrIp": "0.0.0.0/0" }])) # Add EBS volume if local mongo used _data_volume = None if _vpn_config.get('local_mongo', False): self.add_iam_policy( iam.Policy( PolicyName='AttachVolume', PolicyDocument={ 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'ec2:AttachVolume', 'ec2:DeleteSnapshot', 'ec2:DescribeTags', 'ec2:DescribeVolumeAttribute', 'ec2:DescribeVolumeStatus', 'ec2:DescribeVolumes', 'ec2:DetachVolume' ] }] })) _data_volume = ec2.Volume( '{}DataVolume'.format(_vpn_name), Size=_vpn_config.get('data_volume_size', 20), VolumeType='gp2', AvailabilityZone=_vpn_subnet['AvailabilityZone'], DeletionPolicy='Retain', Tags=self.get_tags(service_override=self.service, role_override=_vpn_name) + [ec2.Tag('Name', _vpn_name + "-datavol")]) self.add_resource(_data_volume) # Add the elastic IP and the ENI for it, then attach it. _vpn_eip = self.add_resource( ec2.EIP('{}InstanceEIP'.format(_vpn_name), Domain='vpc')) _vpn_eni = self.add_resource( ec2.NetworkInterface( '{}InstanceENI'.format(_vpn_name), SubnetId=_vpn_subnet['SubnetId'], Description='ENI for {}'.format(_vpn_name), GroupSet=[Ref(_vpn_security_group)] + self.security_groups, SourceDestCheck=False, Tags=self.get_tags(service_override=self.service, role_override=_vpn_name))) self.get_eni_policies() self.add_resource( ec2.EIPAssociation('{}AssociateVPNInstanceENI'.format(_vpn_name), AllocationId=GetAtt(_vpn_eip, "AllocationId"), NetworkInterfaceId=Ref(_vpn_eni))) # Add a route53 DNS name if self.get_partition() != 'aws-us-gov': self.add_resource( route53.RecordSetGroup('{}Route53'.format(_vpn_name), HostedZoneName=constants.ENVIRONMENTS[ self.env]['route53_zone'], RecordSets=[ route53.RecordSet( Name=_public_dns, ResourceRecords=[Ref(_vpn_eip)], Type='A', TTL=600) ])) # Get all route tables in the VPC _vpc_route_tables = self.ec2_conn.describe_route_tables( Filters=[{ 'Name': 'vpc-id', 'Values': [self.vpc_id] }])['RouteTables'] # Set up the routing table for the VPC # Allow for changing client subnets in constants.py for client_subnet in _vpn_config['client_subnets']: for route_table in _vpc_route_tables: self.add_resource( ec2.Route('{}Route{}{}'.format( _vpn_name, client_subnet.translate({ ord("."): "", ord("/"): "" }), route_table['RouteTableId'].replace('-', '')), RouteTableId=route_table['RouteTableId'], DestinationCidrBlock=client_subnet, NetworkInterfaceId=Ref(_vpn_eni))) _mongodb = _vpn_config.get('mongodb') _server_id = _vpn_config['server_id'] _userdata_template = self.get_cloudinit_template( _tpl_name="pritunl_bootstrap" if _bootstrap_mode else None, replacements=(('__PROMPT_COLOR__', self.prompt_color()), ('__SERVER_ID__', _server_id), ('__SERVICE__', self.service), ('__MONGODB__', _mongodb if _mongodb else ''))) _userdata = Sub( _userdata_template.replace( '${', '${!') # Replace bash brackets with CFN escaped style .replace( '{#', '${' ), # Replace rain-style CFN escapes with proper CFN brackets { 'CFN_ENI_ID': Ref(_vpn_eni), 'CFN_EBS_ID': Ref(_data_volume) if _data_volume else '' }) _vpn_launch_configuration = self.add_resource( autoscaling.LaunchConfiguration( '{}LaunchConfiguration'.format(_vpn_name), AssociatePublicIpAddress=True, KeyName=Ref(self.keypair_name), ImageId=Ref(self.ami), InstanceType=Ref(self.instance_type), InstanceMonitoring=False, IamInstanceProfile=Ref(self.instance_profile), UserData=Base64(_userdata))) self.add_resource( autoscaling.AutoScalingGroup( '{}ASGroup'.format(_vpn_name), AvailabilityZones=[_vpn_subnet['AvailabilityZone']], HealthCheckType='EC2', LaunchConfigurationName=Ref(_vpn_launch_configuration), MinSize=0, MaxSize=1, VPCZoneIdentifier=[_vpn_subnet['SubnetId']], Tags=self.get_autoscaling_tags(service_override=self.service, role_override=_vpn_name) + [autoscaling.Tag('Name', _vpn_name, True)]))
DistributionConfig=cloudfront.DistributionConfig( Aliases=[Ref(domain)], Origins=[ cloudfront.Origin(Id=Ref(root_bucket), DomainName=GetAtt(root_bucket, 'DomainName'), S3OriginConfig=cloudfront.S3Origin()) ], DefaultCacheBehavior=cloudfront.DefaultCacheBehavior( Compress=True, ForwardedValues=cloudfront.ForwardedValues(QueryString=False), TargetOriginId=Ref(root_bucket), ViewerProtocolPolicy='redirect-to-https'), DefaultRootObject=Ref(index_page), Enabled=True))) hosted_zone = Join('', [Ref(zone), '.']) template.add_resource( route53.RecordSetGroup('WebsiteDNSRecord', HostedZoneName=hosted_zone, Comment='Records for the root of the hosted zone', RecordSets=[ route53.RecordSet( Name=Ref(domain), Type='A', AliasTarget=route53.AliasTarget( CLOUDFRONT_HOSTED_ZONE_ID, GetAtt(cdn, 'DomainName'))) ])) print(template.to_json())
def configure(self): """ Returns a Nexus template """ self.defaults = {'instance_type': 't3.xlarge'} self.service = 'nexus' self.set_description('Sets up Nexus repository manager servers') self.get_default_security_groups() self.get_standard_parameters() self.get_standard_policies() self.ami = self.add_parameter( Parameter('AMI', Type='String', Description='AMI ID for instances', Default=get_latest_ami_id( self.region, 'amzn2-ami-hvm-2.0.????????-x86_64-gp2', 'amazon'))) config = constants.ENVIRONMENTS[self.env][self.service] # We want the preferred subnet only. subnet = self.get_subnets('private', _preferred_only=True)[0] # Add our security group security_group = self.add_resource( ec2.SecurityGroup( '{}SecurityGroup'.format(self.name), VpcId=self.vpc_id, GroupDescription='Security Group for {}'.format(self.name), SecurityGroupIngress=[ { "IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": constants.SUPERNET }, # HTTP { "IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": constants.SUPERNET }, # HTTPS # {"IpProtocol": "tcp", "FromPort": "8081", "ToPort": "8081", "CidrIp": constants.SUPERNET}, # NexusRM Direct (disabled!) ], SecurityGroupEgress=[{ "IpProtocol": "-1", "FromPort": "-1", "ToPort": "-1", "CidrIp": "0.0.0.0/0" }])) # Add our EBS data volume data_volume = ec2.Volume( '{}DataVolume'.format(self.name), Size=config.get('data_volume_size', 20), VolumeType='gp2', AvailabilityZone=subnet['AvailabilityZone'], DeletionPolicy='Retain', Tags=self.get_tags(service_override=self.service, role_override=self.name) + [ec2.Tag('Name', self.name + "-datavol")]) self.add_resource(data_volume) self.add_iam_policy( iam.Policy(PolicyName='AttachVolume', PolicyDocument={ 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'ec2:AttachVolume', 'ec2:DeleteSnapshot', 'ec2:DescribeTags', 'ec2:DescribeVolumeAttribute', 'ec2:DescribeVolumeStatus', 'ec2:DescribeVolumes', 'ec2:DetachVolume' ] }] })) # Add a ENI for static IP address eni = self.add_resource( ec2.NetworkInterface( '{}InstanceENI'.format(self.name), SubnetId=subnet['SubnetId'], Description='ENI for {}'.format(self.name), GroupSet=[Ref(security_group)] + self.security_groups, SourceDestCheck=True, Tags=self.get_tags(service_override=self.service, role_override=self.name))) self.get_eni_policies() # Add a route53 A record for the main Nexus host route53_zone = constants.ENVIRONMENTS[self.env]['route53_zone'] private_dns = config.get('private_dns', 'nexus.{}'.format(route53_zone)) self.add_resource( route53.RecordSetGroup( '{}Route53'.format(self.name), HostedZoneName=route53_zone, RecordSets=[ route53.RecordSet(Name=private_dns, ResourceRecords=[ GetAtt(eni, 'PrimaryPrivateIpAddress') ], Type='A', TTL=600) ])) # Add CNAME records for each repository, pointing to the main for repository in config['repositories']: self.add_resource( route53.RecordSetGroup( '{}{}Route53'.format(self.name, self.cfn_name(repository)), HostedZoneName=route53_zone, RecordSets=[ route53.RecordSet(Name='{}.{}'.format( repository, route53_zone), ResourceRecords=[private_dns], Type='CNAME', TTL=600) ])) # Add S3 IAM role for nexus blobstore access self.add_iam_policy( iam.Policy( PolicyName='S3Access', PolicyDocument={ 'Statement': [{ "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:GetBucketLocation", "s3:ListBucketMultipartUploads", "s3:ListBucketVersions", "s3:GetBucketAcl", "s3:GetLifecycleConfiguration", "s3:PutLifecycleConfiguration" ], "Resource": [ 'arn:{}:s3:::{}'.format(self.get_partition(), config['s3_bucket']) ] }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts", "s3:GetObjectTagging", "s3:PutObjectTagging", "s3:GetObjectTagging", "s3:DeleteObjectTagging" ], "Resource": [ 'arn:{}:s3:::{}/*'.format(self.get_partition(), config['s3_bucket']) ] }] })) # Substitute the userdata template and feed it to CFN userdata_template = self.get_cloudinit_template(replacements=( ('__PROMPT_COLOR__', self.prompt_color()), ('__SERVICE__', self.service), ('__DEFAULT_DOMAIN__', route53_zone[:-1]), # route53_zone has a trailing '.', strip it ('__TOP_DOMAIN__', constants.ROOT_ROUTE53_ZONE), # ('__REPOSITORIES__', " ".join(['"{}"'.format(x) for x in config['repositories']])) # '"abc" "def" "ghi"' )) userdata = Sub( userdata_template.replace( '${', '${!') # Replace bash brackets with CFN escaped style .replace( '{#', '${' ), # Replace rain-style CFN escapes with proper CFN brackets { 'CFN_ENI_ID': Ref(eni), 'CFN_EBS_ID': Ref(data_volume) }) launch_configuration = self.add_resource( autoscaling.LaunchConfiguration( '{}LaunchConfiguration'.format(self.name), AssociatePublicIpAddress=False, KeyName=Ref(self.keypair_name), ImageId=Ref(self.ami), InstanceType=Ref(self.instance_type), InstanceMonitoring=False, IamInstanceProfile=Ref(self.instance_profile), UserData=Base64(userdata))) self.add_resource( autoscaling.AutoScalingGroup( '{}ASGroup'.format(self.name), AvailabilityZones=[subnet['AvailabilityZone']], HealthCheckType='EC2', LaunchConfigurationName=Ref(launch_configuration), MinSize=0, MaxSize=1, DesiredCapacity=0, VPCZoneIdentifier=[subnet['SubnetId']], Tags=self.get_autoscaling_tags(service_override=self.service, role_override=self.name) + [autoscaling.Tag('Name', self.name, True)]))
def configure(self): config = constants.ENVIRONMENTS[self.env]['mesos']['agent'] self.defaults = { 'instance_type': config.get('instance_type', 'r5.xlarge') } self.add_description('Sets up Mesos Agents in all Zones') self.get_standard_parameters() self.get_standard_policies() self.get_default_security_groups() _global_config = constants.ENVIRONMENTS[self.env] self.ami = self.add_parameter( Parameter( 'AMI', Type='String', Description='AMI ID for instances', Default=get_latest_ami_id(self.region, 'ivy-mesos', _global_config.get('ami_owner', 'self')) ) ) # Mesos Agent Security Group self.mesos_agent_security_group = self.add_resource( ec2.SecurityGroup( 'MesosAgentSecurityGroup', VpcId=self.vpc_id, GroupDescription='Security Group for MesosAgent Instances', SecurityGroupIngress=[ # public http via ELB {'IpProtocol': 'tcp', 'FromPort': 80, 'ToPort': 80, 'CidrIp': self.vpc_cidr}, # internal service SSL direct {'IpProtocol': 'tcp', 'FromPort': 443, 'ToPort': 443, 'CidrIp': self.vpc_cidr}, # host-network services (tcp) {'IpProtocol': 'tcp', 'FromPort': 5000, 'ToPort': 5049, 'CidrIp': self.vpc_cidr}, # host-network services (udp) {'IpProtocol': 'udp', 'FromPort': 5000, 'ToPort': 5049, 'CidrIp': self.vpc_cidr}, # mesos agent api {'IpProtocol': 'tcp', 'FromPort': 5050, 'ToPort': 5051, 'CidrIp': self.vpc_cidr}, # internal http-alt direct {'IpProtocol': 'tcp', 'FromPort': 8000, 'ToPort': 8000, 'CidrIp': self.vpc_cidr}, # internal http via ELB {'IpProtocol': 'tcp', 'FromPort': 8080, 'ToPort': 8080, 'CidrIp': self.vpc_cidr}, # internal http-alt direct {'IpProtocol': 'tcp', 'FromPort': 9090, 'ToPort': 9090, 'CidrIp': self.vpc_cidr}, # mesos tasks (udp) {'IpProtocol': 'udp', 'FromPort': 31000, 'ToPort': 32000, 'CidrIp': self.vpc_cidr}, # mesos tasks (tcp) {'IpProtocol': 'tcp', 'FromPort': 31000, 'ToPort': 32000, 'CidrIp': self.vpc_cidr} ] ) ) self.add_resource( ec2.SecurityGroupIngress( 'MesosAgentIngressSecurityGroup', GroupId=Ref(self.mesos_agent_security_group), IpProtocol='-1', FromPort=-1, ToPort=-1, SourceSecurityGroupId=Ref(self.mesos_agent_security_group) # All Mesos agents can access all ports on each other ) ) self.add_security_group(Ref(self.mesos_agent_security_group)) # Security group for the internet-facing (external) ELBs - not added to the mesos agents themselves self.elb_external_security_group = self.add_resource( ec2.SecurityGroup( 'MesosAgentELBExternalSecurityGroup', VpcId=self.vpc_id, GroupDescription='External Security Group for MesosAgent ELB Instances', SecurityGroupIngress=[ {'IpProtocol': 'tcp', 'FromPort': 80, 'ToPort': 80, 'CidrIp': '0.0.0.0/0'}, # http {'IpProtocol': 'tcp', 'FromPort': 443, 'ToPort': 443, 'CidrIp': '0.0.0.0/0'}, # https {'IpProtocol': 'tcp', 'FromPort': 8443, 'ToPort': 8443, 'CidrIp': '0.0.0.0/0'}, # https-alt {'IpProtocol': 'icmp', 'FromPort': -1, 'ToPort': -1, 'CidrIp': '0.0.0.0/0'} # ping (health checks) ] ) ) # # Docker roles # # Allow assume /docker roles by ec2metaproxy self.add_iam_policy( iam.Policy( PolicyName='AssumeDockerRoles', PolicyDocument={ 'Statement': [ { 'Effect': 'Allow', 'Action': ["sts:AssumeRole"], "Resource": { "Fn::Join": [ "", ["arn:{}:iam::".format(self.get_partition()), {"Ref": "AWS::AccountId"}, ":role/docker/*"] ] }, } ] } ) ) # Add docker roles to assumable roles list for r in self.generate_docker_roles(): self.add_resource(r) # # Load Balancers # lb_type = config.get('lb_type', 'classic') elb_log_bucket = config.get('log_bucket', '{}-{}-logs'.format(constants.TAG, self.env)) if lb_type == 'classic': internal_elb = self.add_resource( self.generate_load_balancer( "{}MesosAgentInternalELB".format(self.env), "internal", 8080, constants.SSL_CERTIFICATES[config['private_elb_cert']]['Arn'], elb_log_bucket ) ) external_elb = self.add_resource( self.generate_load_balancer( "{}MesosAgentExternalELB".format(self.env), "internet-facing", 80, constants.SSL_CERTIFICATES[config['public_elb_cert']]['Arn'], elb_log_bucket ) ) elif lb_type == 'application': internal_elb, internal_target_group = self.generate_app_load_balancer( "{}MesosAgentInternalALB".format(self.env), "internal", 8080, constants.SSL_CERTIFICATES[config['private_elb_cert']]['Arn'], elb_log_bucket ) self.add_resource(internal_elb) self.add_resource(internal_target_group) external_elb, external_target_group = self.generate_app_load_balancer( "{}MesosAgentExternalALB".format(self.env), "internet-facing", 80, constants.SSL_CERTIFICATES[config['public_elb_cert']]['Arn'], elb_log_bucket ) self.add_resource(external_elb) self.add_resource(external_target_group) # extra public load balancers (for SSL termination, ELB doesn't do SNI) extra_public_load_balancers = [] for lb_config in config.get('extra_public_load_balancers', []): if lb_type == 'classic': extra_public_load_balancers.append(Ref(self.add_resource( self.generate_load_balancer( "{}{}MesosAgentExternalELB".format(self.env, lb_config['name']), "internet-facing", 80, constants.SSL_CERTIFICATES[lb_config['cert']]['Arn'], elb_log_bucket ) ))) elif lb_type == 'application': _extra_public_lb, _extra_external_tg = self.generate_app_load_balancer( "{}{}MesosAgentExternalALB".format(self.env, lb_config['name']), "internet-facing", 80, constants.SSL_CERTIFICATES[lb_config['cert']]['Arn'], elb_log_bucket ) self.add_resource(_extra_public_lb) extra_public_load_balancers.append(Ref(self.add_resource(_extra_external_tg))) # # Instances # # Add docker volume block_device_mapping = get_block_device_mapping(self.parameters['InstanceType'].resource['Default']) block_device_mapping.extend([ ec2.BlockDeviceMapping( DeviceName="/dev/xvda", # rootfs Ebs=ec2.EBSBlockDevice( DeleteOnTermination=True, VolumeSize=config.get('rootfs_size', 50), VolumeType="gp2" ) ), ec2.BlockDeviceMapping( DeviceName="/dev/xvdb", Ebs=ec2.EBSBlockDevice( DeleteOnTermination=True, VolumeSize=config.get('dockervol_size', 100), VolumeType=config.get('dockervol_type', 'gp2') ) ) ]) # Launch configurations preferred_only = config.get('preferred_placement', False) if lb_type == 'classic': # Private ASG self.generate_asg("private", count=config['count'].get('private', 2), block_mapping=block_device_mapping, load_balancers=[Ref(internal_elb), Ref(external_elb)] + extra_public_load_balancers, preferred_subnets_only=preferred_only ) # Public ASG self.generate_asg("public", count=config['count'].get('public', 0), block_mapping=block_device_mapping, load_balancers=[Ref(internal_elb), Ref(external_elb)] + extra_public_load_balancers, preferred_subnets_only=preferred_only ) elif lb_type == 'application': # Private ASG self.generate_asg("private", count=config['count'].get('private', 2), block_mapping=block_device_mapping, target_group_arns=[Ref(internal_target_group), Ref(external_target_group)] + extra_public_load_balancers, preferred_subnets_only=preferred_only ) # Public ASG self.generate_asg("public", count=config['count'].get('public', 0), block_mapping=block_device_mapping, target_group_arns=[Ref(internal_target_group), Ref(external_target_group)] + extra_public_load_balancers, preferred_subnets_only=preferred_only ) # # DNS Records # if self.get_partition() != 'aws-us-gov': zone = constants.ENVIRONMENTS[self.env]['route53_zone'] self.add_resource( route53.RecordSetGroup( 'ELBRoute53', HostedZoneName=zone, RecordSets=[ route53.RecordSet( Name='internal.{}'.format(zone)[:-1], ResourceRecords=[GetAtt(internal_elb, 'DNSName')], Type='CNAME', TTL=300 ), route53.RecordSet( Name='external.{}'.format(zone)[:-1], ResourceRecords=[GetAtt(external_elb, 'DNSName')], Type='CNAME', TTL=300 ) ] ) )
def configure(self): rds_metadata = constants.ENVIRONMENTS[self.env]['rds'] self.name = 'rds' self.add_description('Sets up an RDS Instance in a VPC') self.get_standard_parameters() self.get_default_security_groups() for db in rds_metadata: name = self.env + db['name'] # get secrets env_name = "DB_{}_".format(db['name']) db_user = db.get('admin_user', os.environ.get(env_name + "USER", None)) db_pass = db.get('admin_pass', os.environ.get(env_name + "PASS", None)) if (db_user or db_pass) is None: raise KeyError( "Database user or password not set. Please set {0}USER or {0}PASS environment variables" .format(env_name)) if db_user in ("rdsadmin", "admin"): raise ValueError( "Database admin '{}' cannot be used as it is a reserved word used by the engine" .format(db_user)) tags = self.get_tags(service_override=self.name, role_override=db['name']) security_group = self.add_resource( ec2.SecurityGroup( '{}RDSSecurityGroup'.format(name), VpcId=self.vpc_id, GroupDescription='Security Group for {} Access'.format( self.name), SecurityGroupIngress=[{ 'IpProtocol': 'tcp', 'FromPort': 5432, 'ToPort': 5432, 'CidrIp': self.vpc_cidr } # Allow DB access ], Tags=tags)) self.add_security_group(Ref(security_group)) # Default to true for preferred subnet unless using multi_az preferred_only = False if db.get('multi_az') is True else db.get( 'preferred_only', True) rds_subnet_group = self.add_resource( rds.DBSubnetGroup( '{}RDSSubnetGroup'.format(name), DBSubnetGroupDescription='Subnet group for {} RDS'.format( name), SubnetIds=list( map( lambda x: x['SubnetId'], self.get_subnets( 'private', _preferred_only=preferred_only))))) rds_parameter_group = self.add_resource( rds.DBParameterGroup( '{}DBParameterGroup'.format(name), Description='RDS ParameterGroup for {}'.format(name), Family=db.get('engine_family', 'postgres11'), Parameters={ 'log_min_duration_statement': 250, 'max_connections': '{DBInstanceClassMemory/10485760}', 'pg_stat_statements.track': 'all', 'pg_stat_statements.max': db.get('max_logged_statements', '1000') }, Tags=tags)) rds_instance = self.add_resource( rds.DBInstance( '{}RDSInstance'.format(name), AllocatedStorage=db['allocated_storage'], AutoMinorVersionUpgrade=True, BackupRetentionPeriod=7, DBInstanceClass=db['instance_type'], DBInstanceIdentifier=name, DBParameterGroupName=Ref(rds_parameter_group), #DBSnapshotIdentifier=db['snapshot_id'], DBSubnetGroupName=Ref(rds_subnet_group), Engine='postgres', EngineVersion=db.get('engine_version', '11.5'), LicenseModel='postgresql-license', MultiAZ=db.get('multi_az', False), PreferredBackupWindow='06:00-07:00', PreferredMaintenanceWindow='sat:07:00-sat:08:00', PubliclyAccessible=False, StorageEncrypted=True, StorageType='gp2', Tags=tags, VPCSecurityGroups=self.security_groups, MasterUsername=db_user, MasterUserPassword=db_pass, )) if self.get_partition( ) == 'aws': # aws-us-gov and aws-cn may not have route53 public zones hosted_zone = constants.ENVIRONMENTS[self.env]['route53_zone'] self.add_resource( route53.RecordSetGroup( '{}Route53'.format(name), HostedZoneName=hosted_zone, RecordSets=[ route53.RecordSet(Name='{}.rds.{}'.format( db['name'], hosted_zone), ResourceRecords=[ GetAtt( rds_instance, 'Endpoint.Address') ], Type='CNAME', TTL=600) ]))
), ], PriceClass='PriceClass_100', ViewerCertificate=cloudfront.ViewerCertificate( AcmCertificateArn=Ref(certificate), SslSupportMethod='sni-only', )))) record_set_group = template.add_resource( route53.RecordSetGroup( 'RecordSetGroup', HostedZoneId=Ref(hosted_zone_id), RecordSets=[ route53.RecordSet(Name=Ref(domain), Type='A', AliasTarget=route53.AliasTarget( HostedZoneId=CLOUDFRONT_HOSTED_ZONE_ID, DNSName=GetAtt(distribution, 'DomainName'), )), ])) # endregion # region Outputs template.add_output(Output('Distribution', Value=Ref(distribution))) # endregion # region Metadata template.add_metadata({ 'AWS::CloudFormation::Interface': { 'ParameterLabels': { # Project email.title: {