Example #1
0
 def __init__(self, paco_ctx, netenv_ctl, netenv, env, env_region):
     self.paco_ctx = paco_ctx
     self.netenv_ctl = netenv_ctl
     self.netenv = netenv
     self.env = env
     self.env_region = env_region
     self.region = env_region.name
     self.network_stack_grp = None
     self.application_stack_grps = {}
     self.iam_stack_grps = {}
     self.stack_grps = []
     self.account_ctx = paco_ctx.get_account_context(
         account_ref=self.env_region.network.aws_account
     )
     self.init_done = False
     self.resource_yaml_filename = "{}-{}-{}.yaml".format(
         self.netenv.name,
         self.env.name,
         self.env_region.name,
     )
     self.resource_yaml_path = self.paco_ctx.outputs_path / 'NetworkEnvironments'
     self.resource_yaml = self.resource_yaml_path / self.resource_yaml_filename
     self.stack_tags = StackTags()
     self.stack_tags.add_tag('paco.netenv.name', self.netenv.name)
     self.stack_tags.add_tag('paco.env.name', self.env.name)
Example #2
0
    def init_app_monitoring(self):
        "Application level Alarms are not specific to any Resource"
        if getattr(self.config, 'monitoring', None) == None:
            return

        # If health_checks exist, init them
        if getattr(self.config.monitoring, 'health_checks', None) != None and \
            len(self.config.monitoring.health_checks.values()) > 0:
            for health_check in self.config.monitoring.health_checks.values():
                stack_tags = StackTags(self.stack_tags)
                stack_tags.add_tag('Paco-Application-HealthCheck-Name',
                                   health_check.name)
                health_check.resolve_ref_obj = self
                # ToDo: enable other types when there is more than one
                if health_check.type == 'Route53HealthCheck':
                    self.stack_group.add_new_stack(
                        'us-east-1',  # Route53 Health Check only runs in us-east-1
                        self.config,
                        paco.cftemplates.Route53HealthCheck,
                        stack_tags=stack_tags,
                        extra_context={
                            'health_check': health_check,
                            'app_aws_region': self.aws_region,
                        },
                    )

        # If alarm_sets exist init their alarms stack
        if getattr(self.config.monitoring, 'alarm_sets', None) != None and \
            len(self.config.monitoring.alarm_sets.values()) > 0:
            self.stack_group.add_new_stack(self.aws_region,
                                           self.config,
                                           paco.cftemplates.CWAlarms,
                                           change_protected=False,
                                           support_resource_ref_ext='alarms',
                                           stack_tags=self.stack_tags)
Example #3
0
    def init(self, command=None, model_obj=None):
        "Initialize controller"
        if self.init_done:
            return
        # inject the controller into the model
        self.groups.resolve_ref_obj = self
        stack_tags = StackTags()
        # there is no snstopics.yaml for this project
        if self.groups.account == None:
            self.init_done = True
            return
        self.account_ctx = self.paco_ctx.get_account_context(
            account_ref=self.groups.account)

        if self.groups.regions == ['ALL']:
            self.active_regions = self.paco_ctx.project.active_regions
        else:
            self.active_regions = self.groups.regions

        # create a SNSTopicsGroup stack group for each active region
        self.ng_stackgroups = {}
        for region in self.active_regions:
            config_ref = self.groups[region].paco_ref_parts
            stackgroup = SNSTopicsStackGroup(self.paco_ctx, self.account_ctx,
                                             region, 'SNS', self, config_ref,
                                             self.groups[region],
                                             StackTags(stack_tags))
            self.ng_stackgroups[region] = stackgroup
            stackgroup.init()
        self.init_done = True
Example #4
0
    def __init__(
        self,
        account_ctx,
        region,
        resource,
        role_id,
        role,
        stack_group,
        stack_tags,
        template_params,
    ):
        self.account_ctx = account_ctx
        self.region = region
        self.resource = resource
        self.role_id = role_id
        self.role = role
        self.role_ref = role.paco_ref_parts
        self.stack_group = stack_group
        self.stack_tags = stack_tags
        self.template_params = template_params
        self.role_name = None
        self.role_template = None
        self.role_stack = None
        self.policy_context = {}

        # Create a Role stack and add it to the StackGroup
        role_stack_tags = StackTags(self.stack_tags)
        role_stack_tags.add_tag('Paco-IAM-Resource-Type', 'Role')

        # set the resolve_ref_obj on the model
        self.role.resolve_ref_obj = self

        # Resources such as a service might not have change_protected
        change_protected = getattr(self.resource, 'change_protected', False)
        role_ext = get_support_resource_ref_ext(self.resource, self.role)
        self.role_stack = self.stack_group.add_new_stack(
            self.region,
            self.resource,
            IAMRoles,
            account_ctx=self.account_ctx,
            stack_tags=role_stack_tags,
            change_protected=change_protected,
            extra_context={
                'template_params': self.template_params,
                'role': self.role,
            },
            support_resource_ref_ext=role_ext,
        )
        self.role_template = self.role_stack.template
        role_id = self.resource.name + '-' + self.role.name
        self.role_name = self.role_template.gen_iam_role_name(
            "Role", self.role.paco_ref_parts, role_id)
        self.role_arn = "arn:aws:iam::{0}:role/{1}".format(
            self.account_ctx.get_id(), self.role_name)
        role_profile_name = self.role_template.gen_iam_role_name(
            "Profile", self.role.paco_ref_parts, role_id)
        self.role_profile_arn = "arn:aws:iam::{0}:instance-profile/{1}".format(
            self.account_ctx.get_id(), role_profile_name)
Example #5
0
    def add_managed_policy(
        self,
        resource,
        policy_name,
        policy_config_yaml,
        template_params=None,
        change_protected=False,
        extra_ref_names=None,
    ):
        "Adds a Managed Policy stack that is attached to this Role"
        # create a Policy object from YAML and add it to the model
        policy_dict = yaml.load(policy_config_yaml)
        policy_dict['roles'] = [self.role_name]
        # extra_ref_names adds extra parts to the Policy paco.ref
        # This is because the paco.ref is used to generate the a Policy hash used in the AWS
        # Policy name. The ref should not change otherwise the Policy names change.
        parent = resource
        for name in extra_ref_names:
            container = Named(name, parent)
            parent = container
        policy = paco.models.iam.ManagedPolicy(policy_name, parent)
        paco.models.loader.apply_attributes_from_config(policy, policy_dict)

        if policy.paco_ref_parts in self.policy_context.keys():
            print(f"Managed policy already exists: {policy.paco_ref_parts}")
            raise StackException(PacoErrorCode.Unknown)

        # set the resolve_ref_obj to this RoleContext
        policy.resolve_ref_obj = self
        policy_context = {
            'id': policy_name,
            'config': policy,
            'ref': policy.paco_ref_parts,
            'template_params': template_params,
        }
        policy_stack_tags = StackTags(self.stack_tags)
        policy_stack_tags.add_tag('Paco-IAM-Resource-Type', 'ManagedPolicy')
        policy_ext = get_support_resource_ref_ext(resource, policy)
        policy_context['stack'] = self.stack_group.add_new_stack(
            self.region,
            resource,
            IAMManagedPolicies,
            stack_tags=policy_stack_tags,
            extra_context={
                'policy': policy,
                'template_params': template_params
            },
            support_resource_ref_ext=policy_ext)
        policy_context['template'] = policy_context['stack'].template
        self.policy_context['name'] = policy_context[
            'template'].gen_policy_name(policy.name)
        self.policy_context['arn'] = "arn:aws:iam::{0}:policy/{1}".format(
            self.account_ctx.get_id(), self.policy_context['name'])
        self.policy_context[policy.paco_ref_parts] = policy_context
Example #6
0
    def init(self):
        if self.init_done:
            return
        self.init_done = True
        self.paco_ctx.log_start('Init', self.env_region)

        # Secrets Manager
        self.secrets_stack_grp = SecretsManagerStackGroup(
            self.paco_ctx,
            self.account_ctx,
            self,
            self.env_region.secrets_manager,
            StackTags(self.stack_tags)
        )
        self.secrets_stack_grp.init()
        self.stack_grps.append(self.secrets_stack_grp)

        # Network Stack: VPC, Subnets, Etc
        self.network_stack_grp = NetworkStackGroup(
            self.paco_ctx,
            self.account_ctx,
            self,
            StackTags(self.stack_tags)
        )
        self.stack_grps.append(self.network_stack_grp)
        self.network_stack_grp.init()

        # Application Engine Stacks
        for app_name in self.ordered_application_names():
            application_stack_grp = ApplicationStackGroup(
                self.paco_ctx,
                self.account_ctx,
                self,
                self.env_region['applications'][app_name],
                StackTags(self.stack_tags)
            )
            self.application_stack_grps[app_name] = application_stack_grp
            self.stack_grps.append(application_stack_grp)
            application_stack_grp.init()

        # Backup
        if self.env_region.backup_vaults:
            self.backup_stack_grp = BackupVaultsStackGroup(
                self.paco_ctx,
                self.account_ctx,
                self,
                self.env_region.backup_vaults,
                StackTags(self.stack_tags)
            )
            self.backup_stack_grp.init()
            self.stack_grps.append(self.backup_stack_grp)

        self.paco_ctx.log_finish('Init', self.env_region)
Example #7
0
 def __init__(self, paco_ctx, account_ctx, region, group_name, controller,
              resource, topics, stack_tags):
     aws_name = group_name
     super().__init__(paco_ctx, account_ctx, group_name, aws_name,
                      controller)
     self.paco_ctx.log_start('Init', resource)
     stack = self.add_new_stack(
         region,
         resource,
         paco.cftemplates.SNS,
         stack_tags=StackTags(stack_tags),
         extra_context={
             'grp_id': account_ctx.name,
             'topics': topics
         },
         set_resource_stack=True,
     )
     hook_arg = {'resource': resource, 'account_ctx': account_ctx}
     # Hook to create and upload imageDefinitions.json S3 source artifact
     stack.hooks.add(name='StoreResourceSNSState.' + resource.name,
                     stack_action='update',
                     stack_timing='post',
                     hook_method=self.store_resource_sns_state,
                     cache_method=self.store_resource_sns_state_cache,
                     hook_arg=hook_arg)
     stack.hooks.add(name='StoreResourceSNSState.' + resource.name,
                     stack_action='create',
                     stack_timing='post',
                     hook_method=self.store_resource_sns_state,
                     cache_method=self.store_resource_sns_state_cache,
                     hook_arg=hook_arg)
     self.paco_ctx.log_finish('Init', resource)
    def init(self):
        # Initialize resolve_ref_obj and accounts
        accounts = {}
        for app_config in self.config.values():
            for grp_config in app_config.values():
                for secret in grp_config.values():
                    secret.resolve_ref_obj = self
                    if secret.account != None:
                        secret._account_ref = secret.account
                        accounts[secret._account_ref] = True
                        self.secret_account_lookup[
                            secret.paco_ref] = secret._account_ref
                    else:
                        secret._account_ref = self.account_ctx.paco_ref
                        accounts[secret._account_ref] = True
                        self.secret_account_lookup[
                            secret.paco_ref] = secret._account_ref

        for account_ref in accounts.keys():
            self.secrets_stack[account_ref] = self.add_new_stack(
                self.region,
                self.config,
                paco.cftemplates.SecretsManager,
                account_ctx=self.paco_ctx.get_account_context(account_ref),
                stack_tags=StackTags(self.stack_tags),
            )
Example #9
0
 def init(self):
     "init"
     self.paco_ctx.log_start('Init', self.config)
     sns_topics_config = [topic for topic in self.config.values()]
     stack = self.add_new_stack(self.region,
                                self.config,
                                paco.cftemplates.SNSTopics,
                                stack_tags=StackTags(self.stack_tags),
                                extra_context={'grp_id': 'NG'})
     self.paco_ctx.log_finish('Init', self.config)
Example #10
0
    def init(self, command=None, model_obj=None):
        "Initialize SNS Controller"
        if self.init_done:
            return
        stack_tags = StackTags()
        # aggregate SNS Topics grouped by account/region and apply to the computed attr
        default_locations = self.sns.default_locations
        sns_computed = self.sns.computed
        for snstopic in self.sns.topics.values():
            if len(snstopic.locations) == 0:
                locations = default_locations
            else:
                locations = snstopic.locations
            for location in locations:
                account_name = location.account.split('.')[-1]
                if not account_name in sns_computed:
                    sns_computed[account_name] = AccountContainer(
                        account_name, sns_computed)
                    sns_computed[account_name].stackgroups = []
                for region in location.regions:
                    if region not in sns_computed[account_name]:
                        sns_computed[account_name][region] = RegionContainer(
                            region, sns_computed[account_name])
                    snstopic = deepcopy_except_parent(snstopic)
                    snstopic.__parent__ = sns_computed[account_name][region]
                    sns_computed[account_name][region][
                        snstopic.name] = snstopic

        # create a SNSTopicsGroup stack group for each active region
        for account in sns_computed.keys():
            account_ctx = self.paco_ctx.get_account_context(
                account_name=account)
            sns_computed[account].account_ctx = account_ctx
            for region in sns_computed[account].keys():
                topics = sns_computed[account][region].values()
                stackgroup = SNSTopicsStackGroup(self.paco_ctx, account_ctx,
                                                 region, 'SNS', self,
                                                 sns_computed[account][region],
                                                 topics, StackTags(stack_tags))
                sns_computed[account].stackgroups.append(stackgroup)

        self.init_done = True
Example #11
0
 def init(self):
     self.log_init_status('Backup', '', True)
     role = self.create_iam_role()
     for backup_vault in self.config.values():
         backup_vault.resolve_ref_obj = self
         stack = self.add_new_stack(self.region,
                                    backup_vault,
                                    paco.cftemplates.BackupVault,
                                    stack_tags=StackTags(self.stack_tags),
                                    extra_context={'role': role})
         self.stack_list.append(stack)
Example #12
0
 def init(self):
     # Initialize resolve_ref_obj
     for app_config in self.config.values():
         for grp_config in app_config.values():
             for secret_config in grp_config.values():
                 secret_config.resolve_ref_obj = self
     self.secrets_stack = self.add_new_stack(
         self.region,
         self.config,
         paco.cftemplates.SecretsManager,
         stack_tags=StackTags(self.stack_tags),
     )
Example #13
0
 def create_iam_role(self, bucket_name, account_ctx, aws_region):
     "IAM Role for AWS Config"
     iam_role_id = 'AWSConfig-{}'.format(account_ctx.name)
     statements = [{
         'effect':
         "Allow",
         'action': ["s3:PutObject"],
         'resource':
         [f"arn:aws:s3:::{bucket_name}/AWSLogs/{account_ctx.id}/*"],
         'condition': {
             "StringLike": {
                 "s3:x-amz-acl": "bucket-owner-full-control"
             }
         }
     }, {
         'effect': "Allow",
         'action': ["s3:GetBucketAcl"],
         'resource': f"arn:aws:s3:::{bucket_name}"
     }]
     role_dict = {
         'enabled':
         True,
         'path':
         '/',
         'role_name':
         iam_role_id,
         'assume_role_policy': {
             'effect': 'Allow',
             'service': ['config.amazonaws.com']
         },
         'policies': [{
             'name': 'AllowS3BucketWriteAccess',
             'statement': statements
         }],
         'managed_policy_arns':
         ['arn:aws:iam::aws:policy/service-role/AWSConfigRole'],
     }
     role = paco.models.iam.Role(iam_role_id, self.config)
     role.apply_config(role_dict)
     iam_ctl = self.paco_ctx.get_controller('IAM')
     iam_ctl.add_role(region=aws_region,
                      resource=self.config,
                      role=role,
                      iam_role_id=iam_role_id,
                      stack_group=self,
                      stack_tags=StackTags())
     return role
Example #14
0
 def __init__(self, paco_ctx, account_ctx, region, group_name, controller,
              resource, topics, stack_tags):
     aws_name = group_name
     super().__init__(paco_ctx, account_ctx, group_name, aws_name,
                      controller)
     self.paco_ctx.log_start('Init', resource)
     stack = self.add_new_stack(
         region,
         resource,
         paco.cftemplates.SNS,
         stack_tags=StackTags(stack_tags),
         extra_context={
             'grp_id': account_ctx.name,
             'topics': topics
         },
         set_resource_stack=True,
     )
     self.paco_ctx.log_finish('Init', resource)
Example #15
0
 def __init__(self,
              paco_ctx,
              account_ctx,
              aws_region,
              app,
              stack_group,
              ref_type,
              stack_tags=StackTags(),
              env_ctx=None):
     self.paco_ctx = paco_ctx
     self.config = app
     self.app = app
     self.account_ctx = account_ctx
     self.aws_region = aws_region
     self.stack_group = stack_group
     self.ref_type = ref_type
     self.env_ctx = env_ctx
     self.stack_tags = stack_tags
     self.stack_tags.add_tag('Paco-Application-Name', self.app.name)
Example #16
0
    def create_iam_role(self):
        "Backup service Role"
        # if at least one vault is enabled, create an IAM Role
        # BackupVault will create one IAM Role for each NetworkEnvironment/Environment combination,
        # this way a netenv/env can be created, have it's own Role, then a different netenv/env with a second Role
        # if the first netenv/env is deleted, the second one will not be impacted.
        vaults_enabled = False
        for vault in self.config.values():
            if vault.is_enabled():
                vaults_enabled = True
        if not vaults_enabled:
            return None

        netenv = get_parent_by_interface(self.config,
                                         schemas.INetworkEnvironment)
        iam_role_id = 'Backup-{}-{}'.format(netenv.name, self.env_ctx.env.name)
        policy_arns = [
            'arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup',
            'arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForRestores'
        ]
        role_dict = {
            'enabled': True,
            'path': '/',
            'role_name': iam_role_id,
            'managed_policy_arns': policy_arns,
            'assume_role_policy': {
                'effect': 'Allow',
                'service': ['backup.amazonaws.com']
            }
        }
        role = paco.models.iam.Role(iam_role_id, self.config)
        role.apply_config(role_dict)

        iam_ctl = self.paco_ctx.get_controller('IAM')
        iam_ctl.add_role(region=self.env_ctx.region,
                         resource=self.config,
                         role=role,
                         iam_role_id=iam_role_id,
                         stack_group=self,
                         stack_tags=StackTags(self.stack_tags))
        return role
Example #17
0
    def init(self):
        """
        Initializes an Application.

        Creates an EC2LaunchManager then iterates through the Application's ResourceGroups
        in order, then each Resource in that group in order, and calls an init specific
        to each Resource type.

        This will allow each Resource for an Application to do what it needs to be initialized,
        typically creating a CFTemplate for the Resource and adding it to the Application's
        StackGroup, and any supporting CFTemplates needed such as Alarms or IAM Policies.
        """
        self.paco_ctx.log_start('Init', self.config)
        self.ec2_launch_manager = EC2LaunchManager(
            self.paco_ctx, self, self.config, self.account_ctx,
            self.aws_region, self.stack_group, self.stack_tags)

        # Resource Groups
        for grp_id, grp_config in self.config.groups_ordered():
            for res_id, resource in grp_config.resources_ordered():
                # initial resource
                stack_tags = StackTags(self.stack_tags)
                stack_tags.add_tag('Paco-Application-Group-Name', grp_id)
                stack_tags.add_tag('Paco-Application-Resource-Name', res_id)
                resource.resolve_ref_obj = self
                # Create a resource_engine object and initialize it
                resource_engine = getattr(paco.application,
                                          resource.type + 'ResourceEngine',
                                          None)(
                                              self,
                                              grp_id,
                                              res_id,
                                              resource,
                                              StackTags(stack_tags),
                                          )
                #resource_engine.log_init_status()
                resource_engine.init_resource()
                resource_engine.init_monitoring()

        self.init_app_monitoring()
        self.paco_ctx.log_finish('Init', self.config)
Example #18
0
    def init(self):
        # Network Stack Templates
        # VPC Stack
        vpc_config = self.env_ctx.env_region.network.vpc
        if vpc_config == None:
            # NetworkEnvironment with no network - serverless
            return
        network_config = get_parent_by_interface(vpc_config, schemas.INetwork)
        vpc_config.resolve_ref_obj = self
        vpc_config.private_hosted_zone.resolve_ref_obj = self
        self.vpc_stack = self.add_new_stack(
            self.region,
            vpc_config,
            paco.cftemplates.VPC,
            stack_tags=StackTags(self.stack_tags),
        )

        # Segments
        self.segment_list = []
        self.segment_dict = {}
        segments = network_config.vpc.segments
        for segment in segments.values():
            segment.resolve_ref_obj = self
            segment_stack = self.add_new_stack(
                self.region,
                segment,
                paco.cftemplates.Segment,
                stack_tags=StackTags(self.stack_tags),
                stack_orders=[StackOrder.PROVISION],
                extra_context={'env_ctx': self.env_ctx},
            )
            self.segment_dict[segment.name] = segment_stack
            self.segment_list.append(segment_stack)

        # Security Groups
        sg_config = network_config.vpc.security_groups
        self.sg_list = []
        self.sg_dict = {}
        # EC2 NATGateway Security Groups
        # Creates a security group for each Availability Zone in the segment
        sg_nat_id = 'bastion_nat_' + utils.md5sum(str_data='gateway')[:8]
        for nat_config in vpc_config.nat_gateway.values():
            if nat_config.is_enabled() == False:
                continue
            if nat_config.type == 'EC2':
                sg_nat_config_dict = {}
                if sg_nat_id not in sg_config.keys():
                    sg_config[sg_nat_id] = paco.models.networks.SecurityGroups(
                        sg_nat_id, sg_config)
                for az_idx in range(1, network_config.availability_zones + 1):
                    sg_nat_config_dict['enabled'] = True
                    sg_nat_config_dict['ingress'] = []
                    for route_segment in nat_config.default_route_segments:
                        route_segment_id = route_segment.split('.')[-1]
                        az_cidr = getattr(
                            vpc_config.segments[route_segment_id],
                            f"az{az_idx}_cidr")
                        sg_nat_config_dict['ingress'].append({
                            'name': 'SubnetAZ',
                            'cidr_ip': az_cidr,
                            'protocol': '-1'
                        })
                    sg_nat_config_dict['egress'] = [{
                        'name': 'ANY',
                        'cidr_ip': '0.0.0.0/0',
                        'protocol': '-1'
                    }]
                    sg_nat_rule_id = nat_config.name + '_az' + str(az_idx)
                    sg_config[sg_nat_id][
                        sg_nat_rule_id] = paco.models.networks.SecurityGroup(
                            sg_nat_rule_id, vpc_config)
                    paco.models.loader.apply_attributes_from_config(
                        sg_config[sg_nat_id][sg_nat_rule_id],
                        sg_nat_config_dict)

        # Declared Security Groups
        for sg_id in sg_config:
            # Set resolve_ref_obj
            for sg_obj_id in sg_config[sg_id]:
                sg_config[sg_id][sg_obj_id].resolve_ref_obj = self
            sg_stack = self.add_new_stack(
                self.region,
                sg_config[sg_id],
                paco.cftemplates.SecurityGroups,
                stack_tags=StackTags(self.stack_tags),
                extra_context={
                    'env_ctx': self.env_ctx,
                    'template_type': 'Groups'
                },
            )
            self.sg_list.append(sg_stack)
            self.sg_dict[sg_id] = sg_stack

        # Ingress/Egress Stacks
        for sg_id in sg_config:
            self.add_new_stack(self.region,
                               sg_config[sg_id],
                               paco.cftemplates.SecurityGroups,
                               stack_tags=StackTags(self.stack_tags),
                               extra_context={
                                   'env_ctx': self.env_ctx,
                                   'template_type': 'Rules'
                               })

        # Wait for Segment Stacks
        for segment_stack in self.segment_list:
            self.add_stack_order(segment_stack, [StackOrder.WAIT])

        # VPC Peering Stack
        if vpc_config.peering != None:
            peering_config = self.env_ctx.env_region.network.vpc.peering
            for peer_id in peering_config.keys():
                peer_config = vpc_config.peering[peer_id]
                peer_config.resolve_ref_obj = self
                # Add role to the target network account
                if peer_config.network_environment != None and peer_config.peer_type == 'accepter':
                    netenv_ref = Reference(peer_config.network_environment +
                                           '.network')
                    requester_netenv_config = netenv_ref.resolve(
                        self.paco_ctx.project)
                    requester_account_id = self.paco_ctx.get_ref(
                        requester_netenv_config.aws_account + '.id')
                    accepter_vpc_id = self.paco_ctx.get_ref(
                        vpc_config.paco_ref + '.id')
                    # Only create the role if we are cross account
                    if self.account_ctx.id != requester_account_id:
                        self.gen_vpc_peering_accepter_role(
                            peer_config, vpc_config, accepter_vpc_id,
                            requester_account_id)
            self.peering_stack = self.add_new_stack(
                self.region,
                vpc_config.peering,
                paco.cftemplates.VPCPeering,
                stack_tags=StackTags(self.stack_tags),
            )

        # NAT Gateway
        self.nat_list = []
        for nat_config in vpc_config.nat_gateway.values():
            if sg_nat_id in sg_config.keys():
                nat_sg_config = sg_config[sg_nat_id]
            else:
                nat_sg_config = None
            # We now disable the NAT Gateway in the template so that we can delete it and recreate it when disabled.
            nat_stack = self.add_new_stack(
                self.region,
                nat_config,
                paco.cftemplates.NATGateway,
                stack_tags=StackTags(self.stack_tags),
                stack_orders=[StackOrder.PROVISION],
                extra_context={'nat_sg_config': nat_sg_config},
            )
            self.nat_list.append(nat_stack)

        for nat_stack in self.nat_list:
            self.add_stack_order(nat_stack, [StackOrder.WAIT])

        # VPC Endpoints
        vpc_endpoints_stack = self.add_new_stack(
            self.region,
            vpc_config,
            paco.cftemplates.VPCEndpoints,
            stack_tags=StackTags(self.stack_tags),
            stack_orders=[StackOrder.PROVISION])
        self.add_stack_order(vpc_endpoints_stack, [StackOrder.WAIT])
Example #19
0
class EnvironmentRegionContext():
    "EnvironmentRegion Controller-ish"

    def __init__(self, paco_ctx, netenv_ctl, netenv, env, env_region):
        self.paco_ctx = paco_ctx
        self.netenv_ctl = netenv_ctl
        self.netenv = netenv
        self.env = env
        self.env_region = env_region
        self.region = env_region.name
        self.network_stack_grp = None
        self.application_stack_grps = {}
        self.iam_stack_grps = {}
        self.stack_grps = []
        self.account_ctx = paco_ctx.get_account_context(
            account_ref=self.env_region.network.aws_account
        )
        self.init_done = False
        self.resource_yaml_filename = "{}-{}-{}.yaml".format(
            self.netenv.name,
            self.env.name,
            self.env_region.name,
        )
        self.resource_yaml_path = self.paco_ctx.outputs_path / 'NetworkEnvironments'
        self.resource_yaml = self.resource_yaml_path / self.resource_yaml_filename
        self.stack_tags = StackTags()
        self.stack_tags.add_tag('paco.netenv.name', self.netenv.name)
        self.stack_tags.add_tag('paco.env.name', self.env.name)

    @property
    def stack_group_filter(self):
        # The stack_group_filter can change so we need to get it from the network environment itself
        return self.netenv_ctl.stack_group_filter

    def init(self):
        if self.init_done:
            return
        self.init_done = True
        self.paco_ctx.log_start('Init', self.env_region)

        # Secrets Manager
        self.secrets_stack_grp = SecretsManagerStackGroup(
            self.paco_ctx,
            self.account_ctx,
            self,
            self.env_region.secrets_manager,
            StackTags(self.stack_tags)
        )
        self.secrets_stack_grp.init()
        self.stack_grps.append(self.secrets_stack_grp)

        # Network Stack: VPC, Subnets, Etc
        self.network_stack_grp = NetworkStackGroup(
            self.paco_ctx,
            self.account_ctx,
            self,
            StackTags(self.stack_tags)
        )
        self.stack_grps.append(self.network_stack_grp)
        self.network_stack_grp.init()

        # Application Engine Stacks
        for app_name in self.ordered_application_names():
            application_stack_grp = ApplicationStackGroup(
                self.paco_ctx,
                self.account_ctx,
                self,
                self.env_region['applications'][app_name],
                StackTags(self.stack_tags)
            )
            self.application_stack_grps[app_name] = application_stack_grp
            self.stack_grps.append(application_stack_grp)
            application_stack_grp.init()

        # Backup
        if self.env_region.backup_vaults:
            self.backup_stack_grp = BackupVaultsStackGroup(
                self.paco_ctx,
                self.account_ctx,
                self,
                self.env_region.backup_vaults,
                StackTags(self.stack_tags)
            )
            self.backup_stack_grp.init()
            self.stack_grps.append(self.backup_stack_grp)

        self.paco_ctx.log_finish('Init', self.env_region)

    def get_aws_name(self):
        aws_name = '-'.join([self.netenv_ctl.get_aws_name(), self.env.name])
        return aws_name

    def get_vpc_stack(self):
        return self.network_stack_grp.get_vpc_stack()

    def ordered_application_names(self):
        "List of application names sorted according to their order"
        ordered_config_list = []
        ordered_id_list = []
        for app_id, app_config in self.env_region['applications'].items():
            new_app_config = [app_id, app_config]
            insert_idx = 0
            for ordered_config in ordered_config_list:
                if app_config.order < ordered_config[1].order:
                    ordered_config_list.insert(insert_idx, new_app_config)
                    ordered_id_list.insert(insert_idx, app_id)
                    break
                insert_idx += 1
            else:
                ordered_config_list.append(new_app_config)
                ordered_id_list.append(app_id)

        return ordered_id_list

    def save_stack_output_config(self):
        merged_config = {}
        for stack_grp in self.stack_grps:
            for stack in stack_grp.stacks:
                if isinstance(stack, StackGroup):
                    continue
                config_dict = stack.output_config_dict
                if config_dict == None:
                    continue
                merged_config = utils.dict_of_dicts_merge(merged_config, config_dict)

        # Save merged_config to yaml file
        if 'netenv' in merged_config.keys():
            utils.write_to_file(
                folder=self.resource_yaml_path,
                filename=self.resource_yaml_filename,
                data= merged_config['netenv'][self.netenv.name][self.env.name][self.env_region.name]
            )

    def validate(self):
        for stack_grp in self.stack_grps:
            stack_grp.validate()

    def provision(self):
        self.paco_ctx.log_start('Provision', self.env_region)
        # provision EC2LM SSM Document first
        if self.env_region.has_ec2lm_resources():
            ssm_ctl = self.paco_ctx.get_controller('SSM')
            ssm_ctl.provision(f'resource.ssm.ssm_documents.paco_ec2lm_update_instance.{self.account_ctx.name}.{self.env_region.name}')
        if 'paco_ecs_docker_exec' in self.paco_ctx.project['resource']['ssm'].ssm_documents:
            ssm_ctl.provision(f'resource.ssm.ssm_documents.paco_ecs_docker_exec.{self.account_ctx.name}.{self.env_region.name}')
        if len(self.stack_grps) > 0:
            for stack_grp in self.stack_grps:
                stack_grp.provision()
            self.save_stack_output_config()
        else:
            self.paco_ctx.log_action_col("Provision", "Nothing to provision.")
        self.paco_ctx.log_finish('Provision', self.env_region)

    def delete(self):
        for stack_grp in reversed(self.stack_grps):
            stack_grp.delete()