in servicecatalog_puppet/workflow/stack/provision_stack_task.py [0:0]
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,
Tags=self.initialiser_stack_tags,
)
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)