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
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 '-'
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)
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)
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'])
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)
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
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)
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
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)
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
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
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
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)
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
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)
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)
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()
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()
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
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)
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
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
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)
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
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
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
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'])
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
def init(self, command=None, model_obj=None): if self.master_account_config == None: raise StackException(PacoErrorCode.Unknown)