Esempio n. 1
0
    def wait_for_complete(self, verbose=False):
        # While loop to handle expired token retries
        while True:
            if self.action == None:
                return
            self.get_status()
            waiter = None
            action_name = "Provision"
            if self.is_updating():
                if verbose:
                    self.log_action("Provision", "Update")
                waiter = self.cfn_client.get_waiter('stack_update_complete')
            elif self.is_creating():
                if verbose:
                    self.log_action("Provision", "Create")
                waiter = self.cfn_client.get_waiter('stack_create_complete')
            elif self.is_deleting():
                if verbose:
                    self.log_action("Delete", "Stack")
                action_name = "Delete"
                waiter = self.cfn_client.get_waiter('stack_delete_complete')
            elif self.is_complete():
                pass
            elif not self.is_exists():
                pass
            else:
                message = self.get_stack_error_message()
                raise StackException(PacoErrorCode.WaiterError,
                                     message=message)

            if waiter != None:
                self.log_action(action_name, "Wait")
                try:
                    waiter.wait(StackName=self.get_name())
                except WaiterError as waiter_exception:
                    if str(
                            waiter_exception
                    ).find('The security token included in the request is expired'
                           ) != -1:
                        self.handle_token_expired()
                        continue
                    self.log_action(action_name, "Error")
                    message = "Waiter Error:  {}\n".format(waiter_exception)
                    message += self.get_stack_error_message(message)
                    raise StackException(PacoErrorCode.WaiterError,
                                         message=message)
                self.log_action(action_name, "Done")

            if self.is_exists():
                self.stack_success()

            if self.action == "create":
                self.hooks.run("create", "post", self)
            elif self.action == "update":
                self.hooks.run("update", "post", self)
            elif self.action == "delete":
                self.hooks.run("delete", "post", self)

            break
Esempio n. 2
0
    def resource_char_filter(self, ch, filter_id, remove_invalids=False):
        # Duplicated in paco.models.base.Resource
        # Universal check
        if ch.isalnum() == True:
            return ch
        # SecurityGroup Group Name
        # Constraints for EC2-VPC: a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*
        if filter_id == 'SecurityGroup.GroupName':
            if ch in ' ._-:/()#,@[]+=&;{}!$*':
                return ch
        elif filter_id in [
                'IAM.Role.RoleName', 'IAM.ManagedPolicy.ManagedPolicyName',
                'IAM.Policy.PolicyName'
        ]:
            if ch in '_+=,.@-.':
                return ch
        elif filter_id == 'ElastiCache.ReplicationGroup.ReplicationGroupId':
            if ch in '-':
                return ch
        elif filter_id in [
                'EC2.ElasticLoadBalancingV2.LoadBalancer.Name',
                'EC2.ElasticLoadBalancingV2.TargetGroup.Name'
        ]:
            # Only alphanum and dases are allowed
            pass
        else:
            raise StackException(PacoErrorCode.Unknown,
                                 message="Invalid filter Id: " + filter_id)

        if remove_invalids == True:
            return ''

        # By default return a '-' for invalid characters
        return '-'
Esempio n. 3
0
    def init(self, command=None, model_obj=None):
        if self.init_done:
            return
        self.init_done = True
        if command == 'init':
            return

        # currently EC2.yaml only holds keypairs
        # ToDo: enable resource.ec2.keypairs
        if schemas.IEC2Resource.providedBy(model_obj):
            self.keypairs = model_obj.keypairs.values()
        elif schemas.IEC2KeyPairs.providedBy(model_obj):
            self.keypairs = model_obj.values()
        elif schemas.IEC2KeyPair.providedBy(model_obj):
            self.keypairs = [ model_obj ]
        elif model_obj != None:
            raise InvalidPacoScope("Scope of {} not operable.".format(model_obj.paco_ref_parts))

        for keypair in self.keypairs:
            aws_account_ref = keypair.account
            keypair._account_ctx = self.paco_ctx.get_account_context(account_ref=aws_account_ref)
            keypair._ec2_client = keypair._account_ctx.get_aws_client(
                'ec2',
                aws_region=keypair.region
            )
            try:
                keypair._aws_info = keypair._ec2_client.describe_key_pairs(
                    KeyNames=[keypair.keypair_name]
                )['KeyPairs'][0]
            except ClientError as e:
                if e.response['Error']['Code'] == 'InvalidKeyPair.NotFound':
                    pass
                else:
                    # TOOD: Make this more verbose
                    raise StackException(PacoErrorCode.Unknown)
Esempio n. 4
0
 def create_managed_policy(self,
                           paco_ctx,
                           account_ctx,
                           region,
                           group_id,
                           policy_id,
                           policy_ref,
                           policy_config_yaml,
                           parent_config,
                           stack_group,
                           template_params,
                           stack_tags,
                           change_protected=False):
     if policy_ref not in self.policy_context.keys():
         self.policy_context[policy_ref] = PolicyContext(
             paco_ctx=self.paco_ctx,
             account_ctx=account_ctx,
             region=region,
             group_id=group_id,
             policy_id=policy_id,
             policy_ref=policy_ref,
             policy_config_yaml=policy_config_yaml,
             parent_config=parent_config,
             stack_group=stack_group,
             template_params=template_params,
             stack_tags=stack_tags,
             change_protected=change_protected)
     else:
         print("Managed Policy already exists: %s" % (policy_ref))
         raise StackException(PacoErrorCode.Unknown)
Esempio n. 5
0
    def empty_bucket(self):
        if self.bucket_context == None:
            print(
                f"ctl_s3: empty_bucket: ERROR: Unable to locate stack group for group: {self.bucket_context['group_id']}"
            )
            raise StackException(PacoErrorCode.Unknown)
        s3_client = self.account_ctx.get_aws_client('s3')
        bucket_name = self.bucket_context['config'].get_bucket_name()
        try:
            response = s3_client.list_objects_v2(Bucket=bucket_name)
        except ClientError as e:
            if e.response['Error']['Code'] == 'NoSuchBucket':
                return
            else:
                raise e

        if 'Contents' in response:
            for item in response['Contents']:
                s3_client.delete_object(Bucket=bucket_name, Key=item['Key'])
                while response['KeyCount'] == 1000:
                    response = s3_client.list_objects_v2(
                        Bucket=bucket_name,
                        StartAfter=response['Contents'][0]['Key'],
                    )
                    for item in response['Contents']:
                        s3_client.delete_object(Bucket=bucket_name,
                                                Key=item['Key'])
Esempio n. 6
0
 def add_role(self,
              paco_ctx,
              account_ctx,
              region,
              group_id,
              role_id,
              role_ref,
              role_config,
              stack_group,
              template_params,
              stack_tags,
              change_protected=False):
     if role_ref not in self.role_context.keys():
         self.role_context[role_ref] = RoleContext(
             paco_ctx=self.paco_ctx,
             account_ctx=account_ctx,
             region=region,
             group_id=group_id,
             role_id=role_id,
             role_ref=role_ref,
             role_config=role_config,
             stack_group=stack_group,
             template_params=template_params,
             stack_tags=stack_tags,
             change_protected=change_protected)
     else:
         print("Role already exists: %s" % (role_ref))
         raise StackException(PacoErrorCode.Unknown)
Esempio n. 7
0
 def create_role_temp_creds(self, session_creds):
     sts_client = boto3.client(
         'sts',
         region_name=session_creds['AWSDefaultRegion'],
         aws_access_key_id=session_creds['AccessKeyId'],
         aws_secret_access_key=session_creds['SecretAccessKey'],
         aws_session_token=session_creds['SessionToken'],
     )
     for admin_iam_role_arn in [
             self.admin_iam_role_arn, self.org_admin_iam_role_arn
     ]:
         try:
             role_creds = sts_client.assume_role(
                 DurationSeconds=self.assume_role_session_expiry_secs,
                 RoleArn=admin_iam_role_arn,
                 RoleSessionName='paco-multiaccount-session',
                 #TokenCode=token_code,
                 #SerialNumber=self.mfa_arn
             )['Credentials']
         except ClientError as e:
             if admin_iam_role_arn == self.org_admin_iam_role_arn:
                 message = '{}\n'.format(e)
                 message += 'Unable to assume roles: {}\n'.format(
                     self.admin_iam_role_arn)
                 message += '                        {}\n'.format(
                     self.org_admin_iam_role_arn)
                 raise StackException(PacoErrorCode.Unknown,
                                      message=message)
         else:
             self.save_temp_creds(role_creds, self.role_creds_path)
             break
     return role_creds
Esempio n. 8
0
    def get_outputs_value(self, key):

        if key in self.outputs_value_cache.keys():
            return self.outputs_value_cache[key]

        while True:
            try:
                stack_metadata = self.cfn_client.describe_stacks(
                    StackName=self.get_name())
            except ClientError as e:
                if e.response['Error'][
                        'Code'] == 'ValidationError' and e.response['Error'][
                            'Message'].find("does not exist") != -1:
                    message = self.get_stack_error_message()
                    message += 'Could not describe stack to get value for Outputs Key: {}\n'.format(
                        key)
                    message += 'Account: ' + self.account_ctx.get_name()
                    raise StackException(PacoErrorCode.StackDoesNotExist,
                                         message=message)
                elif e.response['Error']['Code'] == 'ExpiredToken':
                    self.handle_token_expired()
                    continue
                else:
                    raise StackException(
                        PacoErrorCode.Unknown,
                        message=e.response['Error']['Message'])
            break

        if 'Outputs' not in stack_metadata['Stacks'][0].keys():
            message = self.get_stack_error_message()
            message += '\nKey: ' + key + '\n'
            message += '\nHints:\n'
            message += '1. register_stack_output_config() calls are missing in the cftemplate.\n'
            message += '2. The CloudFormation template does not have the corresponding Outputs entry.\n'
            message += '3. The stack has not been provisioned yet.\n'
            raise StackException(PacoErrorCode.StackOutputMissing,
                                 message=message)

        for output in stack_metadata['Stacks'][0]['Outputs']:
            if output['OutputKey'] == key:
                self.outputs_value_cache[key] = output['OutputValue']
                return self.outputs_value_cache[key]

        message = self.get_stack_error_message()
        message += "Could not find Stack Output {} in stack_metadata:\n\n{}\n".format(
            key, stack_metadata)
        raise StackException(PacoErrorCode.StackOutputMissing, message=message)
Esempio n. 9
0
    def resolve_ref(self, ref):
        if isinstance(ref.resource, models.applications.SNSTopic):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.TargetGroup):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.ASG):
            if ref.resource_ref.startswith('instance_id'):
                asg_stack = self.stack_group.get_stack_from_ref(ref)
                asg_outputs_key = asg_stack.get_outputs_key_from_ref(ref)
                if asg_outputs_key == None:
                    raise StackException(
                        PacoErrorCode.Unknown,
                        message="Unable to find outputkey for ref: %s" %
                        ref.raw)
                asg_name = asg_stack.get_outputs_value(asg_outputs_key)
                asg_client = self.account_ctx.get_aws_client('autoscaling')
                asg_response = asg_client.describe_auto_scaling_groups(
                    AutoScalingGroupNames=[asg_name])
                instance_id = asg_response['AutoScalingGroups'][0][
                    'Instances'][0]['InstanceId']
                ssm_client = self.account_ctx.get_aws_client('ssm')
                ssm_client.start_session(Target=instance_id)
            else:
                return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.Lambda):
            lambda_stack = self.stack_group.get_stack_from_ref(ref)
            return lambda_stack
        elif isinstance(ref.resource,
                        models.applications.CloudFrontViewerCertificate):
            acm_ctl = self.paco_ctx.get_controller('ACM')
            # Force the region to us-east-1 because CloudFront lives there
            ref.sub_part(ref.region, 'us-east-1')
            ref.region = 'us-east-1'
            return acm_ctl.resolve_ref(ref)
        elif isinstance(ref.resource, models.applications.CloudFrontFactory):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.LBApplication):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.EFS):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.EIP):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.RDS):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.RDSClusterInstance):
            return self.stack_group.get_stack_from_ref(ref)
        elif isinstance(ref.resource, models.applications.EBS):
            return self.stack_group.get_stack_from_ref(ref)
        elif schemas.IDBParameterGroup.providedBy(ref.resource):
            return self.stack_group.get_stack_from_ref(ref)
        elif schemas.IElastiCache.providedBy(ref.resource):
            return self.stack_group.get_stack_from_ref(ref)
        elif schemas.ICodeDeployApplication.providedBy(ref.resource):
            return self.stack_group.get_stack_from_ref(ref)
        elif schemas.IElasticsearchDomain.providedBy(ref.resource):
            return self.stack_group.get_stack_from_ref(ref)

        return None
Esempio n. 10
0
    def save(self, key):
        if self.outputs_path[key] == None:
            raise StackException(PacoErrorCode.Unknown,
                                 message="Outputs file has not been loaded.")

        self.outputs_path[key].parent.mkdir(parents=True, exist_ok=True)

        with open(self.outputs_path[key], "w") as output_fd:
            yaml.dump(self.outputs_dict[key], output_fd)
Esempio n. 11
0
    def get_outputs_key_from_ref(self, ref):

        key = self.template.get_outputs_key_from_ref(ref)
        if key == None:
            message = self.get_stack_error_message()
            message += "Error: Unable to find outputs key for ref: {}\n".format(
                ref.raw)
            raise StackException(PacoErrorCode.Unknown, message=message)
        return key
Esempio n. 12
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
Esempio n. 13
0
    def get_name(self):
        name = '-'.join([self.grp_ctx.get_aws_name(), self.template.aws_name])
        if self.stack_suffix != None:
            name = name + '-' + self.stack_suffix

        new_name = self.create_stack_name(name)

        if new_name[0].isalpha() == False:
            raise StackException(PacoErrorCode.InvalidStackName)

        return new_name
Esempio n. 14
0
 def init_stack_groups(self):
     # TODO: Fixed above now with init done flag?
     if self.second == True:
         raise StackException(PacoErrorCode.Unknown)
     self.second = True
     for account_name in self.config.get_hosted_zones_account_names():
         account_ctx = self.paco_ctx.get_account_context(
             account_name=account_name)
         route53_stack_grp = Route53StackGroup(self.paco_ctx, account_ctx,
                                               self.config, self)
         self.stack_grps.append(route53_stack_grp)
Esempio n. 15
0
def str_spc(str_data, size):
    "Add space padding to a string"
    new_str = str_data
    str_len = len(str_data)
    if str_len > size:
        message = "ERROR: cli: str_spc: string size is larger than space size: {0} > {1}".format(
            str_len, size)
        raise StackException(PacoErrorCode.Unknown, message=message)

    for idx in range(size - str_len):
        new_str += " "
    return new_str
Esempio n. 16
0
 def resolve_ref(self, ref):
     if ref.last_part == 'arn':
         group_id = '.'.join(ref.parts[:-1])
         cert_id = ref.parts[-2]
         res_config = self.get_cert_config(group_id, cert_id)
         if 'cert_arn_cache' in res_config.keys():
             return res_config['cert_arn_cache']
         acm_client = DNSValidatedACMCertClient(
             res_config['account_ctx'], res_config['config'].domain_name,
             ref.region)
         if acm_client:
             cert_arn = acm_client.get_certificate_arn()
             if cert_arn == None:
                 self.provision()
                 cert_arn = acm_client.get_certificate_arn()
             if res_config['config'].external_resource == False:
                 acm_client.wait_for_certificate_validation(cert_arn)
             return cert_arn
         else:
             raise StackException(PacoErrorCode.Unknown)
     raise StackException(PacoErrorCode.Unknown)
Esempio n. 17
0
 def add(self, project_folder, new_outputs_dict):
     if len(new_outputs_dict.keys()) > 1:
         raise StackException(
             PacoErrorCode.Unknown,
             message="Outputs dict should only have one key. Investigate!")
     if len(new_outputs_dict.keys()) == 0:
         return
     key = list(new_outputs_dict.keys())[0]
     self.load(project_folder, key)
     self.outputs_dict[key] = dict_of_dicts_merge(self.outputs_dict[key],
                                                  new_outputs_dict)
     self.save(key)
Esempio n. 18
0
def md5sum(filename=None, str_data=None):
    """Computes and returns an MD5 sum in hexdigest format on a file or string"""
    d = hashlib.md5()
    if filename != None:
        with open(filename, mode='rb') as f:
            for buf in iter(partial(f.read, 128), b''):
                d.update(buf)
    elif str_data != None:
        d.update(bytearray(str_data, 'utf-8'))
    else:
        raise StackException(PacoErrorCode.Unknown, message="cli: md5sum: Filename or String data expected")

    return d.hexdigest()
Esempio n. 19
0
    def __init__(self,
                 paco_ctx,
                 account_ctx,
                 grp_ctx,
                 template,
                 stack_suffix=None,
                 aws_region=None,
                 hooks=None,
                 do_not_cache=False,
                 stack_tags=None,
                 update_only=False,
                 change_protected=False):
        self.paco_ctx = paco_ctx
        self.account_ctx = account_ctx
        self.grp_ctx = grp_ctx
        self.termination_protection = False
        self.stack_suffix = stack_suffix
        if aws_region == None:
            raise StackException(PacoErrorCode.Unknown,
                                 message="AWS Region is not supplied")
        self.aws_region = aws_region
        # Load the template
        template.stack = self
        self.template = template
        self.status = StackStatus.NONE
        self.stack_id = None
        self.cached = False
        self.max_account_name_size = 12
        self.max_action_name_size = 12
        self.output_config_dict = None
        self.action = None
        self.do_not_cache = do_not_cache
        self.update_only = update_only
        self.change_protected = change_protected
        # Wait for stack to delete if this flag is set
        self.wait_for_delete = False

        self.tags = StackTags(stack_tags)
        self.tags.add_tag('Paco-Stack', 'true')
        self.tags.add_tag('Paco-Stack-Name', self.get_name())

        self.outputs_value_cache = {}
        self.singleton = False

        if hooks == None:
            self.hooks = StackHooks(self.paco_ctx)
        else:
            self.hooks = hooks
            self.hooks.stack = self
            self.hooks.log_hooks()
Esempio n. 20
0
    def resource_name_filter(self, name, filter_id, hash_long_names):
        "Checks a name against a filter and raises a StackException if it is not a valid AWS name"
        # Duplicated in paco.models.base.Resource
        message = None
        max_name_len = None
        if filter_id in [
                'EC2.ElasticLoadBalancingV2.LoadBalancer.Name',
                'EC2.ElasticLoadBalancingV2.TargetGroup.Name'
        ]:
            if len(name) > 32:
                max_name_len = 32
                message = "Name must not be longer than 32 characters.",
            elif filter_id.find('LoadBalancer') != -1 and name.startswith(
                    'internal-'):
                message = "Name must not start with 'internal-'"
            elif name[-1] == '-' or name[0] == '-':
                message = "Name must not begin or end with a dash."
        elif filter_id in [
                'IAM.Role.RoleName', 'IAM.ManagedPolicy.ManagedPolicyName'
        ]:
            if len(name) > 255:
                max_name_len = 255
                message = "Name must not be longer than 255 characters."
        elif filter_id == 'IAM.Policy.PolicyName':
            if len(name) > 128:
                max_name_len = 128
                message = "Name must not be longer than 128 characters."
        elif filter_id == 'ElastiCache.ReplicationGroup.ReplicationGroupId':
            if len(name) > 40:
                max_name_len = 255
                message = "ReplicationGroupId must be 40 characters or less"
        elif filter_id == 'SecurityGroup.GroupName':
            pass
        else:
            message = 'Unknown filter_id'

        if max_name_len != None and hash_long_names == True:
            message = None
            name_hash = md5sum(str_data=name)[:8].upper()
            name = name_hash + '-' + name[((max_name_len - 9) * -1):]

        if message != None:
            raise StackException(PacoErrorCode.Unknown,
                                 message="{}: {}: {}: {}".format(
                                     filter_id,
                                     self.config_ref,
                                     message,
                                     name,
                                 ))
        return name
Esempio n. 21
0
    def provision(self):
        self.template.provision()

        # If last md5 is equal, then we no changes are required
        if self.is_stack_cached() == True:
            if self.change_protected:
                self.log_action("Provision", "Protected")
            else:
                self.log_action("Provision", "Cache")
            return

        self.get_status()
        if self.is_failed():
            print("--------------------------------------------------------")
            self.log_action("Provision", "Failed")
            print("The stack is in a '{}' state.".format(self.status))
            stack_message = self.get_stack_error_message(skip_status=True)
            print(stack_message)
            print("--------------------------------------------------------")
            answer = self.paco_ctx.input_confirm_action("\nDelete it?",
                                                        default='y')
            print('')
            if answer:
                self.delete()
                self.wait_for_complete()
            else:
                self.log_action("Provision", "Aborted")
                sys.exit(1)
            self.get_status()

        if self.status == StackStatus.DOES_NOT_EXIST:
            self.create_stack()
        elif self.is_complete():
            self.update_stack()
        elif self.is_creating():
            self.log_action("Provision", "Create")
            self.action = "create"
        elif self.is_deleting():
            self.log_action("Delete", "Stack")
            self.action = "delete"
        elif self.is_updating():
            self.log_action("Provision", "Update")
            self.action = "update"
        elif self.is_creating() == False and self.is_updating() == False:
            self.log_action("Provision", "Error")
            message = self.get_stack_error_message()
            raise StackException(PacoErrorCode.Unknown, message=message)
Esempio n. 22
0
    def get_controller(self, controller_type, command=None, model_obj=None):
        """Gets a controller by name and calls .init() on it with any controller args"""
        controller_type = controller_type.lower()
        controller = None
        if controller_type != 'service':
            if controller_type in self.controllers:
                controller = self.controllers[controller_type]
            if controller == None:
                controller = paco.controllers.klass[controller_type](self)
                self.controllers[controller_type] = controller
        else:
            service_name = model_obj.paco_ref_list[1]
            if service_name.lower() not in self.services:
                message = "Could not find Service: {}".format(service_name)
                raise StackException(PacoErrorCode.Unknown, message = message)
            controller = self.services[service_name.lower()]

        controller.init(command, model_obj)
        return controller
Esempio n. 23
0
 def add_stack(self,
               bucket_policy_only=False,
               stack_hooks=None,
               new_stack=True,
               stack_tags=None,
               change_protected=False):
     s3_template = paco.cftemplates.S3(self.paco_ctx,
                                       self.account_ctx,
                                       self.region,
                                       self.stack_group,
                                       stack_tags,
                                       stack_hooks,
                                       self.bucket_context,
                                       bucket_policy_only,
                                       self.resource_ref,
                                       change_protected=change_protected)
     if bucket_policy_only == False:
         if self.bucket_context['stack'] != None:
             raise StackException(PacoErrorCode.Unknown)
         self.bucket_context['stack'] = s3_template.stack
Esempio n. 24
0
 def stack_hook_pre_delete(self, hook, hook_arg):
     "Empty the S3 Bucket if enabled"
     bucket_context = hook_arg
     s3_config = bucket_context['config']
     s3_resource = self.account_ctx.get_aws_resource('s3', self.region)
     deletion_policy = s3_config.deletion_policy
     bucket_name = s3_config.get_bucket_name()
     if deletion_policy == "delete":
         self.paco_ctx.log_action_col('Run', 'Hook', 'Delete', bucket_name)
         bucket = s3_resource.Bucket(bucket_name)
         try:
             bucket.object_versions.delete()
             bucket.objects.all().delete()
             bucket.delete()
         except botocore.exceptions.ClientError as e:
             if e.response['Error']['Code'] != 'NoSuchBucket':
                 print("%s: %s" % (e.response['Error']['Code'],
                                   e.response['Error']['Message']))
                 raise StackException(PacoErrorCode.Unknown)
     else:
         self.paco_ctx.log_action_col('Run', 'Hook', 'Retain', bucket_name)
Esempio n. 25
0
    def get_role(self, role_id=None, role_ref=None):
        role_by_id = None
        role_by_ref = None
        if role_id != None:
            for role in self.roles:
                if role.id == role_id:
                    role_by_id = role
                    break
        if role_ref != None:
            role_by_ref = role[role_ref]

        if role_by_id != None and role_by_ref != None:
            if role_by_id.id == role_by_ref.id:
                return role_by_id
            else:
                # You specified both role_id and role_ref
                # but they each returned different results.
                raise StackException(PacoErrorCode)
        elif role_by_id != None:
            return role_by_id

        return role_by_ref
Esempio n. 26
0
    def get_account_context(self, account_ref=None, account_name=None, netenv_ref=None):
        if account_ref != None:
            ref = Reference(account_ref)
            account_name = ref.parts[1]
        elif netenv_ref != None:
            account_ref = netenv_ref.split(' ')[1]
            account_ref = 'paco.ref netenv.'+'.'.join(account_ref.split('.', 4)[:-1])+".network.aws_account"
            account_ref = self.get_ref(account_ref)
            return self.get_account_context(account_ref=account_ref)
        elif account_name == None:
            raise StackException(PacoErrorCode.Unknown, message = "get_account_context was only passed None: Not enough context to get account.")

        if account_name in self.accounts:
            return self.accounts[account_name]

        account_ctx = AccountContext(
            paco_ctx=self,
            name=account_name,
            mfa_account=self.master_account
        )
        self.accounts[account_name] = account_ctx

        return account_ctx
Esempio n. 27
0
 def add_stack(
     self,
     bucket_policy_only=False,
     stack_hooks=None,
     new_stack=True,
     stack_tags=None,
 ):
     stack = self.stack_group.add_new_stack(self.region,
                                            self.bucket_context['config'],
                                            paco.cftemplates.S3,
                                            account_ctx=self.account_ctx,
                                            stack_tags=stack_tags,
                                            stack_hooks=stack_hooks,
                                            extra_context={
                                                'bucket_context':
                                                self.bucket_context,
                                                'bucket_policy_only':
                                                bucket_policy_only
                                            })
     if bucket_policy_only == False:
         if self.bucket_context['stack'] != None:
             raise StackException(PacoErrorCode.Unknown)
         self.bucket_context['stack'] = stack
Esempio n. 28
0
    def add_managed_policy(self,
                           parent_config,
                           group_id,
                           policy_id,
                           policy_ref,
                           policy_config_yaml=None,
                           template_params=None,
                           change_protected=False):
        if policy_ref in self.policy_context.keys():
            print("Managed policy already exists: %s" % (policy_ref))
            raise StackException(PacoErrorCode.Unknown)

        policy_config_dict = yaml.load(policy_config_yaml)
        policy_config_dict['roles'] = [self.role_name]
        policy_config = paco.models.iam.ManagedPolicy(policy_id, parent_config)
        paco.models.loader.apply_attributes_from_config(
            policy_config, policy_config_dict)
        policy_config.resolve_ref_obj = self
        policy_context = {
            'id': policy_id,
            'config': policy_config,
            'ref': policy_ref,
            'template_params': template_params
        }
        policy_stack_tags = StackTags(self.stack_tags)
        policy_stack_tags.add_tag('Paco-IAM-Resource-Type', 'ManagedPolicy')
        policy_context['template'] = IAMManagedPolicies(
            self.paco_ctx, self.account_ctx, self.region, self.stack_group,
            policy_stack_tags, policy_context, self.group_id, policy_id,
            change_protected)
        policy_context['stack'] = policy_context['template'].stack
        self.policy_context['name'] = policy_context[
            'template'].gen_policy_name(policy_id)
        self.policy_context['arn'] = "arn:aws:iam::{0}:policy/{1}".format(
            self.account_ctx.get_id(), self.policy_context['name'])
        self.policy_context[policy_ref] = policy_context
        self.stack_group.add_stack_order(policy_context['stack'])
Esempio n. 29
0
    def get_status(self):
        while True:
            try:
                stack_list = self.cfn_client.describe_stacks(
                    StackName=self.get_name())
            except ClientError as e:
                if e.response['Error'][
                        'Code'] == 'ValidationError' and e.response['Error'][
                            'Message'].endswith("does not exist"):
                    self.status = StackStatus.DOES_NOT_EXIST
                elif e.response['Error'][
                        'Code'] == 'ClientError' and e.response['Error'][
                            'Message'].endswith("Rate exceeded"):
                    # Lets try again in a little bit
                    msg_prefix = self.log_action("Provision",
                                                 "Warning",
                                                 return_it=True)
                    print(msg_prefix + ": Get Status throttled")
                    time.sleep(1)
                    continue
                elif e.response['Error']['Code'] == 'ExpiredToken':
                    self.handle_token_expired()
                    continue
                else:
                    message = self.get_stack_error_message(
                        prefix_message=e.response['Error']['Message'],
                        skip_status=True)
                    raise StackException(PacoErrorCode.Unknown,
                                         message=message)
            else:
                self.status = StackStatus[stack_list['Stacks'][0]
                                          ['StackStatus']]
                self.stack_id = stack_list['Stacks'][0]['StackId']
                self.cfn_stack_describe = stack_list['Stacks'][0]

            break
Esempio n. 30
0
 def init(self, command=None, model_obj=None):
     if self.master_account_config == None:
         raise StackException(PacoErrorCode.Unknown)