def update_vpgw_associations(self): remote = set(r['GatewayId'] for r in self.object.get("PropagatingsVgws", [])) local = set() for vgw in self.resource.propagating_vpn_gateways: id = self.runner.get_plan(vgw).resource_id if not id or id not in remote: yield self.generic_action( "Enable route propagation from vpn gateway {}".format( vgw.name), self.client.enable_vgw_route_propagation, RouteTableId=serializers.Identifier(), GatewayId=serializers.Identifier( inner=serializers.Const(vgw)), ) if id: local.add(id) for vgw in remote.difference(local): yield self.generic_action( "Disable route propagation from vpn gateway {}".format(vgw), self.client.disable_vgw_route_propagation, RouteTableId=serializers.Identifier(), GatewayId=serializers.Const(inner=serializers.Const(vgw)), )
def update_object(self): for local_rule in self.resource.ingress: for remote_rule in self.object.get("IpPermissions", []): if local_rule.matches(self.runner, remote_rule): break else: yield self.generic_action( "Authorize ingress {}".format(local_rule), self.client.authorize_security_group_ingress, GroupId=serializers.Identifier(), IpPermissions=serializers.ListOfOne( serializers.Context(serializers.Const(local_rule), serializers.Resource())), ) return for local_rule in self.resource.egress: for remote_rule in self.object.get("IpPermissionsEgress", []): if local_rule.matches(self.runner, remote_rule): break else: yield self.generic_action( "Authorize egress {}".format(local_rule), self.client.authorize_security_group_egress, GroupId=serializers.Identifier(), IpPermissions=serializers.ListOfOne( serializers.Context(serializers.Const(local_rule), serializers.Resource())), )
def update_object(self): for action in super(Apply, self).update_object(): yield action local_rules = self._get_local_rules() remote_rules = self._get_remote_rules() for key, rule in remote_rules.items(): if key not in local_rules or local_rules[key] != rule: yield self.generic_action( "Remove rule {} ({})".format( rule['RuleNumber'], 'egrees' if rule['Egress'] else 'ingress'), self.client.delete_network_acl_entry, NetworkAclId=serializers.Identifier(), RuleNumber=rule['RuleNumber'], Egress=rule['Egress'], ) for key, rule in local_rules.items(): if key not in remote_rules or remote_rules[key] != rule: if rule['Egress']: description = "Add rule: {0[RuleAction]} egress from {0[CidrBlock]}, port {0[PortRange][From]} to {0[PortRange][To]}".format( rule) else: description = "Add rule: {0[RuleAction]} ingress from {0[CidrBlock]}, port {0[PortRange][From]} to {0[PortRange][To]}".format( rule) yield self.generic_action( description, self.client.create_network_acl_entry, NetworkAclId=serializers.Identifier(), **rule)
def update_object(self): if self.resource.route_table: if not self.object.get("RouteTableAssociationId", None): yield self.generic_action( "Associate route table", self.client.associate_route_table, SubnetId=serializers.Identifier(), RouteTableId=serializers.Context( serializers.Argument("route_table"), serializers.Identifier()), ) elif self.object['RouteTableId'] != self.runner.get_plan( self.resource.route_table).resource_id: yield self.generic_action( "Replace route table association", self.client.associate_route_table, AssociationId=self.object["RouteTableAssociationId"], RouteTableId=serializers.Context( serializers.Argument("route_table"), serializers.Identifier()), ) elif self.object.get("RouteTableAssociationId", None): yield self.generic_action( "Disassociate route table", self.client.disassociate_route_table, AssociationId=self.object["RouteTableAssociationId"], ) naa_changed = False if not self.resource.network_acl: return if not self.object: naa_changed = True elif not self.object.get("NetworkAclAssociationId", None): naa_changed = True elif self.runner.get_plan( self.resource.network_acl).resource_id != self.object.get( 'NetworkAclId', None): naa_changed = True if naa_changed: yield self.generic_action( "Replace Network ACL association", self.client.replace_network_acl_association, AssociationId=serializers.Property('NetworkAclAssociationId'), NetworkAclId=serializers.Context( serializers.Argument("network_acl"), serializers.Identifier()), )
def update_object(self): for change in super(Apply, self).update_object(): yield change for attachment in self.object.get("Attachments", []): if attachment['VpcId'] == self.runner.get_plan( self.resource.vpc).resource_id: return yield self.generic_action( "Attach to vpc {}".format(self.resource.vpc), self.client.attach_internet_gateway, InternetGatewayId=serializers.Identifier(), VpcId=serializers.Context(serializers.Argument("vpc"), serializers.Identifier()), )
class Attributes(Resource): resource_name = "attributes" dot_ignore = True idle_timeout = argument.Integer( default=30, field="ConnectionSettings", serializer=serializers.Dict(IdleTimeout=serializers.Identity(), ), ) connection_draining = argument.Integer( default=0, field="ConnectionDraining", serializer=serializers.Dict( Enabled=serializers.Expression(lambda runner, object: object > 0), Timeout=serializers.Identity(), )) cross_zone_load_balancing = argument.Boolean( default=True, field="CrossZoneLoadBalancing", serializer=serializers.Dict(Enabled=serializers.Identity(), )) access_log = argument.Resource( Bucket, field="AccessLog", serializer=serializers.Dict( Enabled=serializers.Expression( lambda runner, object: object is not None), S3BucketName=serializers.Identifier(), ))
def update_attributes(self): if not self.resource.attributes: return a = self.resource.attributes changed = False if not self.object: changed = True else: attributes = self.client.describe_load_balancer_attributes( LoadBalancerName=self.resource_id)['LoadBalancerAttributes'] if attributes['ConnectionSettings'][ 'IdleTimeout'] != a.idle_timeout: changed = True if attributes['ConnectionDraining'][ 'Timeout'] != a.connection_draining: changed = True if attributes['CrossZoneLoadBalancing'][ 'Enabled'] != a.cross_zone_load_balancing: changed = True if attributes['AccessLog'].get('S3BucketName', None) != a.access_log: changed = True if changed: yield self.generic_action( "Configure attributes", self.client.modify_load_balancer_attributes, LoadBalancerName=serializers.Identifier(), LoadBalancerAttributes=serializers.Context( serializers.Const(a), serializers.Resource()), )
def update_object(self): policy_names = [] for change in super(Apply, self).update_object(): yield change remote_policy = self.object.get('AssumeRolePolicyDocument', None) if remote_policy and remote_policy != self.resource.assume_role_policy: yield self.generic_action( "Update 'Assume Role Policy' document", self.client.update_assume_role_policy, RoleName=serializers.Identifier(), PolicyDocument=serializers.Json(serializers.Argument("assume_role_policy")), ) # If the object exists then we can look at the roles it has # Otherwise we assume its a new role and it will have no policies if self.object: policy_names = self.client.list_role_policies( RoleName=self.resource.name, )['PolicyNames'] for name, document in self.resource.policies.items(): document = json.loads(document) changed = False if name not in policy_names: changed = True # We can't do a single API to get all names and documents for all # policies, so for each policy that *might* have changed we have to # call teh API and check. # Save an API call by only doing it for policies that definitely # exist if not changed: policy = self.client.get_role_policy( RoleName=self.resource.name, PolicyName=name, ) if policy['PolicyDocument'] != document: changed = True if changed: yield self.generic_action( "Put policy {}".format(name), self.client.put_role_policy, RoleName=self.resource.name, PolicyName=name, PolicyDocument=json.dumps(document), ) for name in policy_names: if name not in self.resource.policies: yield self.generic_action( "Delete policy {}".format(name), self.client.delete_role_policy, RoleName=self.resource.name, PolicyName=name, )
def update_object(self): if not self.object: yield self.generic_action( "Attach gateway to vpc", self.client.attach_vpn_gateway, VpnGatewayId=serializers.Identifier(), VpcId=serializers.Context(serializers.Argument("vpc"), serializers.Identifer()), )
def update_object(self): remote_routes = set(r['DestinationCidrBlock'] for r in self.object.get('Routes', []) if r['State'] != 'deleted') local_routes = set(self.resource.static_routes) for route in local_routes.difference(remote_routes): yield self.generic_action( "Add missing route {}".format(route), self.client.create_vpn_connection_route, VpnConnectionId=serializers.Identifier(), DestinationCidrBlock=route, ) for route in remote_routes.difference(local_routes): yield self.generic_action( "Remove stale route {}".format(route), self.client.create_vpn_connection_route, VpnConnectionId=serializers.Identifier(), DestinationCidrBlock=route, )
def update_routes(self): """ Compare the individual routes listed in the RouteTable to the ones defined in the current workspace, creating and removing routes as needed. Old routes are removed *before* new routes are added. This may cause connection glitches when applied, but it avoids route collisions. """ remote_routes = list(d for d in self.object.get("Routes", []) if d["GatewayId"] != "local") if remote_routes: for remote in remote_routes: for local in self.resource.routes: if local.matches(self.runner, remote): break else: yield self.generic_action( "Remove route for {}".format( remote['DestinationCidrBlock']), self.client.delete_route, RouteTableId=serializers.Identifier(), DestinationCidrBlock=remote['DestinationCidrBlock'], ) if self.resource.routes: for local in self.resource.routes: for remote in remote_routes: if local.matches(self.runner, remote): break else: yield self.generic_action( "Adding route for {}".format(local.destination_cidr), self.client.create_route, serializers.Context( serializers.Const(local), serializers.Resource( RouteTableId=serializers.Identifier( serializers.Const(self.resource)), )))
class S3Origin(Resource): resource_name = "s3_origin" extra_serializers = { "S3OriginConfig": serializers.Dict(OriginAccessIdentity=serializers.Argument( "origin_access_identity"), ), } name = argument.String(field='Id') bucket = argument.Resource(Bucket, field="DomainName", serializer=serializers.Format( "{0}.s3.amazonaws.com", serializers.Identifier())) origin_access_identity = argument.String(default='')
class AutoScalingGroup(Resource): resource_name = "auto_scaling_group" name = argument.String(field="AutoScalingGroupName") launch_configuration = argument.Resource(LaunchConfiguration, field="LaunchConfigurationName") min_size = argument.Integer(field="MinSize") max_size = argument.Integer(field="MaxSize") desired_capacity = argument.Integer(field="DesiredCapacity") default_cooldown = argument.Integer(default=300, field="DefaultCooldown") availability_zones = argument.List( field="AvailabilityZones", serializer=serializers.List(skip_empty=True)) subnets = argument.ResourceList( Subnet, field="VPCZoneIdentifier", serializer=serializers.CommaSeperatedList( serializers.List(serializers.Identifier())), ) load_balancers = argument.ResourceList(LoadBalancer, field="LoadBalancerNames", aws_update=False) health_check_type = argument.String( max=32, default=lambda instance: "ELB" if instance.load_balancers else None, field="HealthCheckType", ) health_check_grace_period = argument.Integer( default=lambda instance: 480 if instance.load_balancers else None, field="HealthCheckGracePeriod", ) placement_group = argument.String(max=255, field="PlacementGroup") termination_policies = argument.List(default=lambda i: ["Default"], field="TerminationPolicies") replacement_policy = argument.String(choices=['singleton', 'graceful'], default='graceful') account = argument.Resource(BaseAccount)
def get_destroy_serializer(self): return serializers.Dict( ReplicationGroupId=serializers.Identifier(), RetainPrimaryCluster=True if self.resource.primary_cluster else False, )
class Rule(Resource): resource_name = "rule" @property def dot_ignore(self): return self.security_group is None protocol = argument.String(default='tcp', choices=['tcp', 'udp', 'icmp'], field="IpProtocol") port = argument.Integer(min=-1, max=32768) from_port = argument.Integer(default=lambda r: r.port, min=-1, max=32768, field="FromPort") to_port = argument.Integer(default=lambda r: r.port, min=-1, max=32768, field="ToPort") security_group = argument.Resource( "touchdown.aws.vpc.security_group.SecurityGroup", field="UserIdGroupPairs", serializer=serializers.ListOfOne( serializers.Dict( UserId=serializers.Property("OwnerId"), GroupId=serializers.Identifier(), )), ) network = argument.IPNetwork( field="IpRanges", serializer=serializers.ListOfOne( serializers.Dict(CidrIp=serializers.String(), )), ) def matches(self, runner, rule): sg = None if self.security_group: sg = runner.get_plan(self.security_group) # If the SecurityGroup doesn't exist yet then this rule can't exist # yet - so we can bail early! if not sg.resource_id: return False if self.protocol != rule['IpProtocol']: return False if self.from_port != rule.get('FromPort', None): return False if self.to_port != rule.get('ToPort', None): return False if sg and sg.object: for group in rule.get('UserIdGroupPairs', []): if group['GroupId'] == sg.resource_id and group[ 'UserId'] == sg.object['OwnerId']: return True if self.network: for network in rule.get('IpRanges', []): if network['CidrIp'] == str(self.network): return True return False def __str__(self): name = super(Rule, self).__str__() if self.from_port == self.to_port: ports = "port {}".format(self.from_port) else: ports = "ports {} to {}".format(self.from_port, self.to_port) return "{}: {} {} from {}".format( name, self.protocol, ports, self.network if self.network else self.security_group)
def update_object(self): changes = [] description = ["Update hosted zone records"] # Retrieve all DNS records associated with this hosted zone # Ignore SOA and NS records for the top level domain remote_records = [] if self.resource_id: for record in self.client.list_resource_record_sets( HostedZoneId=self.resource_id)['ResourceRecordSets']: if record['Type'] in ( 'SOA', 'NS') and record['Name'] == self.resource.name: continue remote_records.append(record) for local in self.resource.records: for remote in remote_records: if local.matches(self.runner, remote): break else: changes.append( serializers.Dict( Action="UPSERT", ResourceRecordSet=serializers.Context( serializers.Const(local), serializers.Resource()), )) description.append("Name => {}, Type={}, Action=UPSERT".format( local.name, local.type)) if not self.resource.shared: for remote in remote_records: for local in self.resource.records: if remote["Name"] != local.name: continue if remote["Type"] != local.type: continue if remote.get("SetIdentifier", None) != local.set_identifier: continue break else: changes.append( serializers.Const({ "Action": "DELETE", "ResourceRecordSet": record })) description.append( "Name => {}, Type={}, Action=DELETE".format( record["Name"], record["Type"])) if changes: yield self.generic_action( description, self.client.change_resource_record_sets, serializers.Dict( HostedZoneId=serializers.Identifier(), ChangeBatch=serializers.Dict( #Comment="", Changes=serializers.Context( serializers.Const(changes), serializers.List(serializers.SubSerializer())), )), )