Ejemplo n.º 1
0
    def run(self):
        logger.info(
            f"[{self.launch_name}] {self.account_id}:{self.region} :: "
            f"starting deploy try {self.try_count} of {self.retry_count}")

        all_params = {}

        logger.info(
            f"[{self.launch_name}] {self.account_id}:{self.region} :: collecting ssm params"
        )
        for ssm_param_name, ssm_param in self.input().get('ssm_params',
                                                          {}).items():
            all_params[ssm_param_name] = ssm_param.read()

        logger.info(
            f"[{self.launch_name}] {self.account_id}:{self.region} :: collecting manifest params"
        )
        for parameter in self.parameters:
            all_params[parameter.get('name')] = parameter.get('value')

        role = f"arn:aws:iam::{self.account_id}:role/servicecatalog-puppet/PuppetRole"
        with betterboto_client.CrossAccountClientContextManager(
                'servicecatalog',
                role,
                f'sc-{self.region}-{self.account_id}',
                region_name=self.region) as service_catalog:
            logger.info(
                f"[{self.launch_name}] {self.account_id}:{self.region} :: looking for previous failures"
            )
            provisioned_product_id, provisioning_artifact_id = aws.terminate_if_status_is_not_available(
                service_catalog, self.launch_name, self.product_id,
                self.account_id, self.region)
            logger.info(
                f"[{self.launch_name}] {self.account_id}:{self.region} :: "
                f"provisioned_product_id: {provisioned_product_id}, "
                f"provisioning_artifact_id : {provisioning_artifact_id}")

            with betterboto_client.CrossAccountClientContextManager(
                    'cloudformation',
                    role,
                    f'cfn-{self.region}-{self.account_id}',
                    region_name=self.region) as cloudformation:
                need_to_provision = True
                if provisioned_product_id:
                    default_cfn_params = aws.get_default_parameters_for_stack(
                        cloudformation,
                        f"SC-{self.account_id}-{provisioned_product_id}")
                else:
                    default_cfn_params = {}

                for default_cfn_param_name in default_cfn_params.keys():
                    if all_params.get(default_cfn_param_name) is None:
                        all_params[
                            default_cfn_param_name] = default_cfn_params[
                                default_cfn_param_name]
                if provisioning_artifact_id == self.version_id:
                    logger.info(
                        f"[{self.launch_name}] {self.account_id}:{self.region} :: found previous good provision"
                    )
                    if provisioned_product_id:
                        logger.info(
                            f"[{self.launch_name}] {self.account_id}:{self.region} :: checking params for diffs"
                        )
                        provisioned_parameters = aws.get_parameters_for_stack(
                            cloudformation,
                            f"SC-{self.account_id}-{provisioned_product_id}")
                        logger.info(
                            f"[{self.launch_name}] {self.account_id}:{self.region} :: "
                            f"current params: {provisioned_parameters}")

                        logger.info(
                            f"[{self.launch_name}] {self.account_id}:{self.region} :: "
                            f"new params: {all_params}")

                        if provisioned_parameters == all_params:
                            logger.info(
                                f"[{self.launch_name}] {self.account_id}:{self.region} :: params unchanged"
                            )
                            need_to_provision = False
                        else:
                            logger.info(
                                f"[{self.launch_name}] {self.account_id}:{self.region} :: params changed"
                            )

                if need_to_provision:
                    logger.info(
                        f"[{self.launch_name}] {self.account_id}:{self.region} :: about to provision with "
                        f"params: {json.dumps(all_params)}")

                    if provisioned_product_id:
                        with betterboto_client.CrossAccountClientContextManager(
                                'cloudformation',
                                role,
                                f'cfn-{self.region}-{self.account_id}',
                                region_name=self.region) as cloudformation:
                            stack = aws.get_stack_output_for(
                                cloudformation,
                                f"SC-{self.account_id}-{provisioned_product_id}"
                            )
                            stack_status = stack.get('StackStatus')
                            logger.info(
                                f"[{self.launch_name}] {self.account_id}:{self.region} :: current cfn stack_status is "
                                f"{stack_status}")
                            if stack_status not in [
                                    "UPDATE_COMPLETE", "CREATE_COMPLETE"
                            ]:
                                raise Exception(
                                    f"[{self.launch_name}] {self.account_id}:{self.region} :: current cfn stack_status is "
                                    f"{stack_status}")

                    provisioned_product_id = aws.provision_product(
                        service_catalog,
                        self.launch_name,
                        self.account_id,
                        self.region,
                        self.product_id,
                        self.version_id,
                        self.puppet_account_id,
                        aws.get_path_for_product(service_catalog,
                                                 self.product_id),
                        all_params,
                        self.version,
                    )

                f = self.output().open('w')
                with betterboto_client.CrossAccountClientContextManager(
                        'cloudformation',
                        role,
                        f'cfn-{self.region}-{self.account_id}',
                        region_name=self.region) as cloudformation:
                    f.write(
                        json.dumps(
                            aws.get_stack_output_for(
                                cloudformation,
                                f"SC-{self.account_id}-{provisioned_product_id}"
                            ),
                            indent=4,
                            default=str,
                        ))
                f.close()
                logger.info(
                    f"[{self.launch_name}] {self.account_id}:{self.region} :: finished provisioning"
                )
    def run(self):
        logger.info(
            f"[{self.uid}] starting deploy try {self.try_count} of {self.retry_count}"
        )

        product_id, version_id = self.get_product_and_version_ids()

        all_params = self.get_all_params()

        role = f"arn:aws:iam::{self.account_id}:role/servicecatalog-puppet/PuppetRole"
        with betterboto_client.CrossAccountClientContextManager(
                'servicecatalog',
                role,
                f'sc-{self.region}-{self.account_id}',
                region_name=self.region) as service_catalog:
            logger.info(f"[{self.uid}] looking for previous failures")
            path_id = aws.get_path_for_product(service_catalog, product_id,
                                               self.portfolio)

            provisioned_product_id, provisioning_artifact_id = aws.terminate_if_status_is_not_available(
                service_catalog, self.launch_name, product_id, self.account_id,
                self.region)
            logger.info(
                f"[{self.uid}] pp_id: {provisioned_product_id}, paid : {provisioning_artifact_id}"
            )

            with betterboto_client.CrossAccountClientContextManager(
                    'cloudformation',
                    role,
                    f'cfn-{self.region}-{self.account_id}',
                    region_name=self.region) as cloudformation:
                need_to_provision = True

                logging.info(
                    f"running as {role},checking {product_id} {version_id} {path_id} in {self.account_id} {self.region}"
                )

                with self.input().get('provisioning_artifact_parameters').open(
                        'r') as f:
                    provisioning_artifact_parameters = json.loads(f.read())

                params_to_use = {}
                for p in provisioning_artifact_parameters:
                    param_name = p.get('ParameterKey')
                    params_to_use[param_name] = all_params.get(
                        param_name, p.get('DefaultValue'))

                if provisioning_artifact_id == version_id:
                    logger.info(f"[{self.uid}] found previous good provision")
                    if provisioned_product_id:
                        logger.info(f"[{self.uid}] checking params for diffs")
                        provisioned_parameters = aws.get_parameters_for_stack(
                            cloudformation,
                            f"SC-{self.account_id}-{provisioned_product_id}")
                        logger.info(
                            f"[{self.uid}] current params: {provisioned_parameters}"
                        )

                        logger.info(
                            f"[{self.uid}] new params: {params_to_use}")

                        if provisioned_parameters == params_to_use:
                            logger.info(f"[{self.uid}] params unchanged")
                            need_to_provision = False
                        else:
                            logger.info(f"[{self.uid}] params changed")

                if need_to_provision:
                    logger.info(
                        f"[{self.uid}] about to provision with params: {json.dumps(params_to_use)}"
                    )

                    if provisioned_product_id:
                        with betterboto_client.CrossAccountClientContextManager(
                                'cloudformation',
                                role,
                                f'cfn-{self.region}-{self.account_id}',
                                region_name=self.region) as cloudformation:
                            stack = aws.get_stack_output_for(
                                cloudformation,
                                f"SC-{self.account_id}-{provisioned_product_id}"
                            )
                            stack_status = stack.get('StackStatus')
                            logger.info(
                                f"[{self.uid}] current cfn stack_status is {stack_status}"
                            )
                            if stack_status not in [
                                    "UPDATE_COMPLETE", "CREATE_COMPLETE",
                                    "UPDATE_ROLLBACK_COMPLETE"
                            ]:
                                raise Exception(
                                    f"[{self.uid}] current cfn stack_status is {stack_status}"
                                )
                            if stack_status == "UPDATE_ROLLBACK_COMPLETE":
                                logger.warning(
                                    f"[{self.uid}] SC-{self.account_id}-{provisioned_product_id} has a status of "
                                    f"{stack_status}.  This may need manual resolution."
                                )

                    if provisioned_product_id:
                        if self.should_use_product_plans:
                            provisioned_product_id = aws.provision_product_with_plan(
                                service_catalog,
                                self.launch_name,
                                self.account_id,
                                self.region,
                                product_id,
                                version_id,
                                self.puppet_account_id,
                                path_id,
                                params_to_use,
                                self.version,
                                self.should_use_sns,
                            )
                        else:
                            provisioned_product_id = aws.update_provisioned_product(
                                service_catalog,
                                self.launch_name,
                                self.account_id,
                                self.region,
                                product_id,
                                version_id,
                                self.puppet_account_id,
                                path_id,
                                params_to_use,
                                self.version,
                            )

                    else:
                        provisioned_product_id = aws.provision_product(
                            service_catalog,
                            self.launch_name,
                            self.account_id,
                            self.region,
                            product_id,
                            version_id,
                            self.puppet_account_id,
                            path_id,
                            params_to_use,
                            self.version,
                            self.should_use_sns,
                        )

                with betterboto_client.CrossAccountClientContextManager(
                        'cloudformation',
                        role,
                        f'cfn-{self.region}-{self.account_id}',
                        region_name=self.region) as spoke_cloudformation:
                    stack_details = aws.get_stack_output_for(
                        spoke_cloudformation,
                        f"SC-{self.account_id}-{provisioned_product_id}")

                for ssm_param_output in self.ssm_param_outputs:
                    logger.info(
                        f"[{self.uid}] writing SSM Param: {ssm_param_output.get('stack_output')}"
                    )
                    with betterboto_client.ClientContextManager('ssm') as ssm:
                        found_match = False
                        for output in stack_details.get('Outputs', []):
                            if output.get('OutputKey') == ssm_param_output.get(
                                    'stack_output'):
                                found_match = True
                                logger.info(f"[{self.uid}] found value")
                                ssm.put_parameter(
                                    Name=ssm_param_output.get('param_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')}"
                            )

                for p in self.post_actions:
                    yield portfoliomanagement.ProvisionActionTask(**p)

                with self.output().open('w') as f:
                    f.write(json.dumps(
                        stack_details,
                        indent=4,
                        default=str,
                    ))
                logger.info(f"[{self.uid}] finished provisioning")
    def run(self):
        details = self.load_from_input("details")
        product_id = details.get("product_details").get("ProductId")
        version_id = details.get("version_details").get("Id")

        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()

        with self.spoke_regional_client("servicecatalog") as service_catalog:
            path_name = self.portfolio

            (
                provisioned_product_id,
                provisioning_artifact_id,
                provisioned_product_status,
            ) = aws.terminate_if_status_is_not_available(
                service_catalog,
                self.launch_name,
                product_id,
                self.account_id,
                self.region,
                self.should_delete_rollback_complete_stacks,
            )
            self.info(
                f"pp_id: {provisioned_product_id}, paid : {provisioning_artifact_id}"
            )

            with self.spoke_regional_client("cloudformation") as cloudformation:
                need_to_provision = True

                self.info(
                    f"running ,checking {product_id} {version_id} {path_name} in {self.account_id} {self.region}"
                )

                with self.input().get("provisioning_artifact_parameters").open(
                    "r"
                ) as f:
                    provisioning_artifact_parameters = json.loads(f.read())

                params_to_use = {}
                for p in provisioning_artifact_parameters:
                    param_name = p.get("ParameterKey")
                    params_to_use[param_name] = all_params.get(
                        param_name, p.get("DefaultValue")
                    )

                if provisioning_artifact_id == version_id:
                    self.info(f"found previous good provision")
                    if provisioned_product_id:
                        self.info(f"checking params for diffs")
                        pp_stack_name = aws.get_stack_name_for_pp_id(
                            service_catalog, provisioned_product_id
                        )
                        provisioned_parameters = aws.get_parameters_for_stack(
                            cloudformation, pp_stack_name,
                        )
                        self.info(f"current params: {provisioned_parameters}")

                        self.info(f"new params: {params_to_use}")

                        if provisioned_parameters == params_to_use:
                            self.info(f"params unchanged")
                            need_to_provision = False
                        else:
                            self.info(f"params changed")

                if provisioned_product_status == "TAINTED":
                    need_to_provision = True

                if need_to_provision:
                    self.info(
                        f"about to provision with params: {json.dumps(tasks.unwrap(params_to_use))}"
                    )

                    if provisioned_product_id:
                        pp_stack_name = aws.get_stack_name_for_pp_id(
                            service_catalog, provisioned_product_id
                        )
                        stack = aws.get_stack_output_for(cloudformation, pp_stack_name,)
                        stack_status = stack.get("StackStatus")
                        self.info(f"current cfn stack_status is {stack_status}")
                        if stack_status not in [
                            "UPDATE_COMPLETE",
                            "CREATE_COMPLETE",
                            "UPDATE_ROLLBACK_COMPLETE",
                        ]:
                            raise Exception(
                                f"[{self.uid}] current cfn stack_status is {stack_status}"
                            )
                        if stack_status == "UPDATE_ROLLBACK_COMPLETE":
                            self.warning(
                                f"[{self.uid}] {pp_stack_name} has a status of "
                                f"{stack_status}.  This may need manual resolution."
                            )

                    if provisioned_product_id:
                        if self.should_use_product_plans:
                            path_id = aws.get_path_for_product(
                                service_catalog, product_id, self.portfolio
                            )
                            provisioned_product_id = aws.provision_product_with_plan(
                                service_catalog,
                                self.launch_name,
                                self.account_id,
                                self.region,
                                product_id,
                                version_id,
                                self.puppet_account_id,
                                path_id,
                                params_to_use,
                                self.version,
                                self.should_use_sns,
                            )
                        else:
                            provisioned_product_id = aws.update_provisioned_product(
                                service_catalog,
                                self.launch_name,
                                self.account_id,
                                self.region,
                                product_id,
                                version_id,
                                self.puppet_account_id,
                                path_name,
                                params_to_use,
                                self.version,
                                self.execution,
                            )

                    else:
                        provisioned_product_id = aws.provision_product(
                            service_catalog,
                            self.launch_name,
                            self.account_id,
                            self.region,
                            product_id,
                            version_id,
                            self.puppet_account_id,
                            path_name,
                            params_to_use,
                            self.version,
                            self.should_use_sns,
                            self.execution,
                        )

                self.info(f"self.execution is {self.execution}")
                if self.execution in [
                    constants.EXECUTION_MODE_HUB,
                    constants.EXECUTION_MODE_SPOKE,
                ]:
                    self.info(
                        f"Running in execution mode: {self.execution}, checking for SSM outputs"
                    )
                    outputs = service_catalog.get_provisioned_product_outputs(
                        ProvisionedProductId=provisioned_product_id
                    ).get("Outputs", [])
                    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 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
                                    self.info(f"found value")
                                    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)
                self.info("finished")