def test_consistency(): pairs = [ (MASTER, CLUSTER), (CLUSTER, SCHEDULER), (CLUSTER, WEBSERVER), (CLUSTER, WORKERSET), ] for (t_outer, t_inner) in pairs: for param1, specs1 in t_outer["Parameters"].items(): for param2, specs2 in t_inner["Parameters"].items(): if param1 == param2: assert (param1, dump_yaml(specs1)) == (param2, dump_yaml(specs2))
def test_dump_yaml(): """ YAML dumping needs to use quoted style for strings with newlines, use a standard indenting style, and preserve order """ source = ODict(( ( "z", "short string", ), ( "m", { "Ref": "embedded string" }, ), ( "a", "A\nmulti-line\nstring", ), )) actual = dump_yaml(source) assert actual == """z: short string
def test_should_return_security_group_with_egress_cidr_rule(self): self.__given_a_session() self.__given_a_security_group_with_egress_cidr_rule() self.__when_a_security_group_is_created_to_use_cidr() result = yaml.load(dump_yaml(load_yaml(self.template.to_yaml())), Loader=yaml.FullLoader) self.assertEqual( result['Resources']['TestRule']['Properties']['CidrIp'], self.cidr)
def test_should_return_security_group_with_egress_sg_name_rule(self): self.__given_a_session() self.__given_a_security_group_with_egress_sg_name_rule() self.__when_a_security_group_is_created_to_use_sg_name() result = yaml.load(dump_yaml(load_yaml(self.template.to_yaml())), Loader=yaml.FullLoader) self.assertEqual( result['Resources']['TestRule']['Properties'] ['DestinationSecurityGroupId']['Ref'], self.sg_name)
def generate(self, template_path): with open(template_path, 'r') as template: cfn_yaml = load_yaml(template.read()) parameter_groups = cfn_yaml['Metadata'][ 'AWS::CloudFormation::Interface']['ParameterGroups'] parameter_labels = cfn_yaml['Metadata'][ 'AWS::CloudFormation::Interface']['ParameterLabels'] parameters = cfn_yaml['Parameters'] print( dump_yaml({ "ParameterGroups": parameter_groups, "ParameterLabels": parameter_labels, "Parameters": parameters }))
def list_resources(): click.echo("# Framework resources") click.echo("## SSM Parameters used") click.echo(f"- {constants.CONFIG_PARAM_NAME}") click.echo(f"- {constants.CONFIG_PARAM_NAME_ORG_IAM_ROLE_ARN}") for file in Path(__file__).parent.resolve().glob("*.template.yaml"): if 'empty.template.yaml' == file.name: continue template_contents = Template(open(file, 'r').read()).render() template = cfn_tools.load_yaml(template_contents) click.echo(f"## Resources for stack: {file.name.split('.')[0]}") table_data = [ [ 'Logical Name', 'Resource Type', 'Name', ], ] table = terminaltables.AsciiTable(table_data) for logical_name, resource in template.get('Resources').items(): resource_type = resource.get('Type') name = '-' type_to_name = { 'AWS::IAM::Role': 'RoleName', 'AWS::SSM::Parameter': 'Name', 'AWS::S3::Bucket': 'BucketName', 'AWS::CodePipeline::Pipeline': 'Name', 'AWS::CodeBuild::Project': 'Name', 'AWS::CodeCommit::Repository': 'RepositoryName', 'AWS::SNS::Topic': 'TopicName', 'AWS::SQS::Queue': 'QueueName', } if type_to_name.get(resource_type) is not None: name = resource.get('Properties', {}).get(type_to_name.get(resource_type), 'Not Specified') if not isinstance(name, str): name = cfn_tools.dump_yaml(name) table_data.append([logical_name, resource_type, name]) click.echo(table.table) click.echo( f"n.b. AWS::StackName evaluates to {constants.BOOTSTRAP_STACK_NAME}")
def from_yaml(cls, template_path: Union[str, Path], imports: Optional[Dict[str, str]] = None) -> Template: """Loads a Cloudformation template from file. Args: template_path (Union[str, Path]): The path to the template. imports (Optional[Dict[str, str]], optional): Values this template plans to import from other stacks exports. Defaults to None. Returns: Template: A Template object ready for testing. """ with open(template_path) as f: raw = f.read() tmp_yaml = load_yaml(raw) tmp_str = dump_yaml(tmp_yaml) template = yaml.load(tmp_str, Loader=yaml.FullLoader) return cls(template, imports)
def run(self): stack = self.ensure_stack_is_in_complete_status() status = stack.get("StackStatus") with self.spoke_regional_client("cloudformation") as cloudformation: if status == "ROLLBACK_COMPLETE": if self.should_delete_rollback_complete_stacks: cloudformation.ensure_deleted( StackName=self.stack_name_to_use) else: raise Exception( f"Stack: {self.stack_name_to_use} is in ROLLBACK_COMPLETE and need remediation" ) task_output = dict( **self.params_for_results_display(), account_parameters=tasks.unwrap(self.account_parameters), launch_parameters=tasks.unwrap(self.launch_parameters), manifest_parameters=tasks.unwrap(self.manifest_parameters), ) all_params = self.get_parameter_values() template_to_provision_source = self.input().get("template").open( "r").read() try: template_to_provision = cfn_tools.load_yaml( template_to_provision_source) except Exception: try: template_to_provision = cfn_tools.load_json( template_to_provision_source) except Exception: raise Exception("Could not parse new template as YAML or JSON") params_to_use = dict() for param_name, p in template_to_provision.get("Parameters", {}).items(): if all_params.get(param_name, p.get("DefaultValue")) is not None: params_to_use[param_name] = all_params.get( param_name, p.get("DefaultValue")) existing_stack_params_dict = dict() existing_template = "" if status in [ "CREATE_COMPLETE", "UPDATE_ROLLBACK_COMPLETE", "UPDATE_COMPLETE", "IMPORT_COMPLETE", "IMPORT_ROLLBACK_COMPLETE", ]: with self.spoke_regional_client( "cloudformation") as cloudformation: existing_stack_params_dict = {} summary_response = cloudformation.get_template_summary( StackName=self.stack_name_to_use, ) for parameter in summary_response.get("Parameters"): existing_stack_params_dict[parameter.get( "ParameterKey")] = parameter.get("DefaultValue") for stack_param in stack.get("Parameters", []): existing_stack_params_dict[stack_param.get( "ParameterKey")] = stack_param.get("ParameterValue") template_body = cloudformation.get_template( StackName=self.stack_name_to_use, TemplateStage="Original").get("TemplateBody") try: existing_template = cfn_tools.load_yaml(template_body) except Exception: try: existing_template = cfn_tools.load_json(template_body) except Exception: raise Exception( "Could not parse existing template as YAML or JSON" ) template_to_use = cfn_tools.dump_yaml(template_to_provision) if status == "UPDATE_ROLLBACK_COMPLETE": need_to_provision = True else: if existing_stack_params_dict == params_to_use: self.info(f"params unchanged") if template_to_use == cfn_tools.dump_yaml(existing_template): self.info(f"template the same") need_to_provision = False else: self.info(f"template changed") need_to_provision = True else: self.info(f"params changed") need_to_provision = True if need_to_provision: provisioning_parameters = [] for p in params_to_use.keys(): provisioning_parameters.append({ "ParameterKey": p, "ParameterValue": params_to_use.get(p) }) with self.spoke_regional_client( "cloudformation") as cloudformation: a = dict( StackName=self.stack_name_to_use, TemplateBody=template_to_use, ShouldUseChangeSets=False, Capabilities=self.capabilities, Parameters=provisioning_parameters, ShouldDeleteRollbackComplete=self. should_delete_rollback_complete_stacks, ) if self.use_service_role: a["RoleARN"] = config.get_puppet_stack_role_arn( self.account_id) cloudformation.create_or_update(**a) task_output["provisioned"] = need_to_provision self.info(f"self.execution is {self.execution}") if self.execution == constants.EXECUTION_MODE_HUB: self.info( f"Running in execution mode: {self.execution}, checking for SSM outputs" ) if len(self.ssm_param_outputs) > 0: with self.spoke_regional_client( "cloudformation") as spoke_cloudformation: stack_details = aws.get_stack_output_for( spoke_cloudformation, self.stack_name_to_use, ) for ssm_param_output in self.ssm_param_outputs: self.info( f"writing SSM Param: {ssm_param_output.get('stack_output')}" ) with self.hub_client("ssm") as ssm: found_match = False # TODO push into another task for output in stack_details.get("Outputs", []): if output.get("OutputKey") == ssm_param_output.get( "stack_output"): ssm_parameter_name = ssm_param_output.get( "param_name") ssm_parameter_name = ssm_parameter_name.replace( "${AWS::Region}", self.region) ssm_parameter_name = ssm_parameter_name.replace( "${AWS::AccountId}", self.account_id) found_match = True ssm.put_parameter_and_wait( Name=ssm_parameter_name, Value=output.get("OutputValue"), Type=ssm_param_output.get( "param_type", "String"), Overwrite=True, ) if not found_match: raise Exception( f"[{self.uid}] Could not find match for {ssm_param_output.get('stack_output')}" ) self.write_output(task_output) else: self.write_output(task_output)
sourceName = str(sys.argv[1]) addonName = str(sys.argv[2]) outputName = sourceName source = open(sourceName, 'r').read() addon = open(addonName, 'r').read() addonObj = load_yaml(addon) sourceObj = load_yaml(source) sourceKeys = sourceObj.keys() res = sourceObj['Resources'] if ('TeamAdminPermissionBoundaryPolicy' in res): temp = res['TeamAdminPermissionBoundaryPolicy']['Properties'][ 'PolicyDocument']['Statement'] temp.append(addonObj) sourceObj['Resources']['TeamAdminPermissionBoundaryPolicy']['Properties'][ 'PolicyDocument']['Statement'] = temp if ('TeamWidePermissionBoundaryPolicy' in res): temp = res['TeamWidePermissionBoundaryPolicy']['Properties'][ 'PolicyDocument']['Statement'] temp.append(addonObj) sourceObj['Resources']['TeamWidePermissionBoundaryPolicy']['Properties'][ 'PolicyDocument']['Statement'] = temp '''sourceObj.update(addonObj) sourceObj.move_to_end('Outputs', last=True) print(sourceObj)''' output = dump_yaml(sourceObj) outputFile = open(outputName, "w") outputFile.write(output) outputFile.close()
def run(self): status = self.get_current_status() if status == "-": self.write_result( "-", self.version_id, effect=constants.CHANGE, current_status="-", active="N/A", notes="Stack would be created", ) elif status == "ROLLBACK_COMPLETE": if self.should_delete_rollback_complete_stacks: self.write_result( "-", self.version_id, effect=constants.CHANGE, current_status="-", active="N/A", notes="Stack would be replaced", ) else: self.write_result( "-", "-", effect=constants.NO_CHANGE, current_status="-", active="N/A", notes="Stack needs remediation - it's in ROLLBACK_COMPLETE", ) else: task_output = dict( **self.params_for_results_display(), account_parameters=tasks.unwrap(self.account_parameters), launch_parameters=tasks.unwrap(self.launch_parameters), manifest_parameters=tasks.unwrap(self.manifest_parameters), ) all_params = self.get_parameter_values() template_to_provision_source = self.input().get("template").open( "r").read() try: template_to_provision = cfn_tools.load_yaml( template_to_provision_source) except Exception: try: template_to_provision = cfn_tools.load_json( template_to_provision_source) except Exception: raise Exception( "Could not parse new template as YAML or JSON") params_to_use = dict() for param_name, p in template_to_provision.get("Parameters", {}).items(): if all_params.get(param_name, p.get("DefaultValue")) is not None: params_to_use[param_name] = all_params.get( param_name, p.get("DefaultValue")) existing_stack_params_dict = dict() existing_template = "" if status in [ "CREATE_COMPLETE", "UPDATE_ROLLBACK_COMPLETE", "UPDATE_COMPLETE", "IMPORT_COMPLETE", "IMPORT_ROLLBACK_COMPLETE", ]: with self.spoke_regional_client( "cloudformation") as cloudformation: existing_stack_params_dict = {} stack = cloudformation.describe_stacks( StackName=self.stack_name).get("Stacks")[0] summary_response = cloudformation.get_template_summary( StackName=self.stack_name, ) for parameter in summary_response.get("Parameters"): existing_stack_params_dict[parameter.get( "ParameterKey")] = parameter.get("DefaultValue") for stack_param in stack.get("Parameters", []): existing_stack_params_dict[stack_param.get( "ParameterKey")] = stack_param.get( "ParameterValue") template_body = cloudformation.get_template( StackName=self.stack_name, TemplateStage="Original").get("TemplateBody") try: existing_template = cfn_tools.load_yaml(template_body) except Exception: try: existing_template = cfn_tools.load_json( template_body) except Exception: raise Exception( "Could not parse existing template as YAML or JSON" ) template_to_use = cfn_tools.dump_yaml(template_to_provision) if status == "UPDATE_ROLLBACK_COMPLETE": self.write_result( "?", self.version_id, effect=constants.CHANGE, current_status=status, active="N/A", notes="Stack would be updated", ) else: if existing_stack_params_dict == params_to_use: self.info(f"params unchanged") if template_to_use == cfn_tools.dump_yaml( existing_template): self.info(f"template the same") self.write_result( "?", self.version_id, effect=constants.NO_CHANGE, current_status=status, active="N/A", notes="No change", ) else: self.info(f"template changed") self.write_result( "?", self.version_id, effect=constants.CHANGE, current_status=status, active="N/A", notes="Template has changed", ) else: self.info(f"params changed") self.write_result( "?", self.version_id, effect=constants.CHANGE, current_status=status, active="N/A", notes="Parameters have changed", )
def test_run(self): # setup mocked_output = MagicMock() self.sut.output = mocked_output # exercise self.sut.run() # verify template = load_yaml( mocked_output().open().__enter__().write.mock_calls[0][1][0]) found = 0 statements = (template.get("Resources").get("TopicPolicies").get( "Properties").get("PolicyDocument").get("Statement", [])) for statement in statements: if statement.get("Sid") == "ShareFor01234567890": found += 1 self.assertEqual( "Fn::Sub: arn:${AWS::Partition}:iam::01234567890:root", dump_yaml(statement.get("Principal").get("AWS")).strip(), ) if statement.get("Sid") == "OrganizationalShareForou-0932u0jsdj": found += 1 self.assertEqual( "ou-0932u0jsdj", statement.get("Condition").get("StringEquals").get( "aws:PrincipalOrgID"), ) statements = (template.get("Resources").get("BucketPolicies").get( "Properties").get("PolicyDocument").get("Statement", [])) for statement in statements: if statement.get("Sid") == "ShareFor01234567890": found += 1 self.assertEqual( "Fn::Sub: arn:${AWS::Partition}:iam::01234567890:root", dump_yaml(statement.get("Principal").get("AWS")).strip(), ) if statement.get("Sid") == "OrganizationalShareForou-0932u0jsdj": found += 1 self.assertEqual( "ou-0932u0jsdj", statement.get("Condition").get("StringEquals").get( "aws:PrincipalOrgID"), ) self.assertDictEqual( dict( Type="AWS::Events::EventBusPolicy", Condition="RunningInHomeRegion", Properties=dict( EventBusName="servicecatalog-puppet-event-bus", Action="events:PutEvents", Principal="01234567890", StatementId="AllowSpokesAccounts01234567890", ), ), template.get("Resources").get(f"EventBusPolicy01234567890"), ) self.assertDictEqual( dict( Type="AWS::Events::EventBusPolicy", Condition="RunningInHomeRegion", Properties=dict( EventBusName="servicecatalog-puppet-event-bus", Action="events:PutEvents", Principal="*", StatementId="AllowSpokesOrgsou-0932u0jsdj", Condition=dict( Type="StringEquals", Key="aws:PrincipalOrgID", Value="ou-0932u0jsdj", ), ), ), template.get("Resources").get(f"EventBusPolicyou0932u0jsdj"), ) self.assertEqual(4, found)
def save_template(template_yaml, save_path): file = open(save_path, "w+") file.write(dump_yaml(template_yaml)) file.close()
#!/usr/bin/env python """Script to convert AWS SAM templates to AWS CloudFormation templates. Expects SAM template on stdin and prints out CloudFormation template on stdout. """ import sys import boto3 import yaml from cfn_tools import dump_yaml from samtranslator.public.translator import ManagedPolicyLoader from samtranslator.translator.transform import transform input_fragment = yaml.load(sys.stdin.read()) iam_client = boto3.client('iam') transformed = transform(input_fragment, {}, ManagedPolicyLoader(iam_client)) print(dump_yaml(transformed))
print("Transforming Template: ", sys.argv[1]) print("Bucket Name: ", sys.argv[2]) print("Bucket Prefix: ", sys.argv[3]) function_names = [] with open(sys.argv[1]) as file: text = file.read() data = load_yaml(text) resources = get_resources(data) for resource in resources.items(): for prop in resource: if isinstance(prop, str): # Logical resource names get skipped continue print(prop) if prop['Type'] == 'Custom::ContactFlow': uri = prop['Properties']['ContactFlowUri'] new_uri_object = process_contact_flow(uri) prop['Properties']['ContactFlowPath'] = new_uri_object[ 'file_path'] prop['Properties']['BuildNumber'] = sys.argv[3] prop['Properties']['ContactFlowBucket'] = new_uri_object[ 'bucket'] new_file = open(sys.argv[1], "w") new_file.write(dump_yaml(data)) new_file.close()