def run()

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)