def deploy_organization()

in rdk/rdk.py [0:0]


    def deploy_organization(self):
        self.__parse_deploy_organization_args()

        #get the rule names
        rule_names = self.__get_rule_list_for_command()

        #run the deploy code
        print ("Running Organization deploy!")

        #create custom session based on whatever credentials are available to us
        my_session = self.__get_boto_session()

        #get accountID
        identity_details = self.__get_caller_identity_details(my_session)
        account_id = identity_details['account_id']
        partition = identity_details['partition']

        if self.args.custom_code_bucket:
            code_bucket_name = self.args.custom_code_bucket
        else:
            code_bucket_name = code_bucket_prefix + account_id + "-" + my_session.region_name

        #If we're only deploying the Lambda functions (and role + permissions), branch here.  Someday the "main" execution path should use the same generated CFN templates for single-account deployment.
        if self.args.functions_only:
            print ("We don't handle Function Only deployment for Organizations")
            sys.exit(1)

        #If we're deploying both the functions and the Config rules, run the following process:
        for rule_name in rule_names:
            rule_params, cfn_tags = self.__get_rule_parameters(rule_name)

            #create CFN Parameters common for Managed and Custom
            source_events = "NONE"
            if "Remediation" in rule_params:
                print(f"WARNING: Organization Rules with Remediation is not supported at the moment. {rule_name} will be deployed without auto-remediation.")

            if 'SourceEvents' in rule_params:
                source_events = rule_params['SourceEvents']

            source_periodic = "NONE"
            if 'SourcePeriodic' in rule_params:
                source_periodic = rule_params['SourcePeriodic']

            combined_input_parameters = {}
            if 'InputParameters' in rule_params:
                combined_input_parameters.update(json.loads(rule_params['InputParameters']))

            if 'OptionalParameters' in rule_params:
                #Remove empty parameters
                keys_to_delete = []
                optional_parameters_json = json.loads(rule_params['OptionalParameters'])
                for key, value in optional_parameters_json.items():
                    if not value:
                        keys_to_delete.append(key)
                for key in keys_to_delete:
                    del optional_parameters_json[key]
                combined_input_parameters.update(optional_parameters_json)

            if 'SourceIdentifier' in rule_params:
                print("Found Managed Rule.")
                #create CFN Parameters for Managed Rules

                try:
                    rule_description = rule_params["Description"]
                except KeyError:
                    rule_description = rule_name
                my_params = [
                    {
                        'ParameterKey': 'RuleName',
                        'ParameterValue': rule_name,
                    },
                    {
                        'ParameterKey': 'Description',
                        'ParameterValue': rule_description,
                    },
                    {
                        'ParameterKey': 'SourceEvents',
                        'ParameterValue': source_events,
                    },
                    {
                        'ParameterKey': 'SourcePeriodic',
                        'ParameterValue': source_periodic,
                    },
                    {
                        'ParameterKey': 'SourceInputParameters',
                        'ParameterValue': json.dumps(combined_input_parameters),
                    },
                    {
                        'ParameterKey': 'SourceIdentifier',
                        'ParameterValue': rule_params['SourceIdentifier']
                    }]
                my_cfn = my_session.client('cloudformation')

                #deploy config rule
                cfn_body = os.path.join(path.dirname(__file__), 'template',  "configManagedRuleOrganization.json")

                try:
                    my_stack_name = self.__get_stack_name_from_rule_name(rule_name)
                    my_stack = my_cfn.describe_stacks(StackName=my_stack_name)
                    #If we've gotten here, stack exists and we should update it.
                    print ("Updating CloudFormation Stack for " + rule_name)
                    try:
                        cfn_args = {
                            'StackName': my_stack_name,
                            'TemplateBody': open(cfn_body, "r").read(),
                            'Parameters': my_params
                        }

                        # If no tags key is specified, or if the tags dict is empty
                        if cfn_tags is not None:
                            cfn_args['Tags'] = cfn_tags

                        response = my_cfn.update_stack(**cfn_args)
                    except ClientError as e:
                        if e.response['Error']['Code'] == 'ValidationError':
                            if 'No updates are to be performed.' in str(e):
                                #No changes made to Config rule definition, so CloudFormation won't do anything.
                                print("No changes to Config Rule.")
                            else:
                                #Something unexpected has gone wrong.  Emit an error and bail.
                                print(e)
                                return 1
                        else:
                            raise
                except ClientError as e:
                    #If we're in the exception, the stack does not exist and we should create it.
                    print ("Creating CloudFormation Stack for " + rule_name)
                    cfn_args = {
                        'StackName': my_stack_name,
                        'TemplateBody': open(cfn_body, "r").read(),
                        'Parameters': my_params
                    }

                    if cfn_tags is not None:
                        cfn_args['Tags'] = cfn_tags

                    response = my_cfn.create_stack(**cfn_args)

                #wait for changes to propagate.
                self.__wait_for_cfn_stack(my_cfn, my_stack_name)

                #Cloudformation is not supporting tagging config rule currently.
                if cfn_tags is not None and len(cfn_tags) > 0:
                    print("WARNING: Tagging is not supported for organization config rules. Only the cloudformation template will be tagged.")

                continue

            print("Found Custom Rule.")

            s3_src = ""
            s3_dst = self.__upload_function_code(rule_name, rule_params, account_id, my_session, code_bucket_name)

            #create CFN Parameters for Custom Rules
            lambdaRoleArn = ""
            if self.args.lambda_role_arn:
                print ("Existing IAM Role provided: " + self.args.lambda_role_arn)
                lambdaRoleArn = self.args.lambda_role_arn
            elif self.args.lambda_role_name:
                print (f"[{my_session.region_name}]: Building IAM Role ARN from Name: " + self.args.lambda_role_name)
                arn = f"arn:{partition}:iam::{account_id}:role/{self.args.lambda_role_name}"
                lambdaRoleArn = arn


            if self.args.boundary_policy_arn:
                print ("Boundary Policy provided: " + self.args.boundary_policy_arn)
                boundaryPolicyArn = self.args.boundary_policy_arn
            else:
                boundaryPolicyArn = ""

            try:
                rule_description = rule_params["Description"]
            except KeyError:
                rule_description = rule_name

            my_params = [
                {
                    'ParameterKey': 'RuleName',
                    'ParameterValue': rule_name,
                },
                {
                    'ParameterKey': 'RuleLambdaName',
                    'ParameterValue': self.__get_lambda_name(rule_name, rule_params),
                },
                {
                    'ParameterKey': 'Description',
                    'ParameterValue': rule_description,
                },
                {
                    'ParameterKey': 'LambdaRoleArn',
                    'ParameterValue': lambdaRoleArn,
                },
                {
                    'ParameterKey': 'BoundaryPolicyArn',
                    'ParameterValue': boundaryPolicyArn,
                },
                {
                    'ParameterKey': 'SourceBucket',
                    'ParameterValue': code_bucket_name,
                },
                {
                    'ParameterKey': 'SourcePath',
                    'ParameterValue': s3_dst,
                },
                {
                    'ParameterKey': 'SourceRuntime',
                    'ParameterValue': self.__get_runtime_string(rule_params),
                },
                {
                    'ParameterKey': 'SourceEvents',
                    'ParameterValue': source_events,
                },
                {
                    'ParameterKey': 'SourcePeriodic',
                    'ParameterValue': source_periodic,
                },
                {
                    'ParameterKey': 'SourceInputParameters',
                    'ParameterValue': json.dumps(combined_input_parameters),
                },
                {
                    'ParameterKey': 'SourceHandler',
                    'ParameterValue': self.__get_handler(rule_name, rule_params)

                },
                {
                    'ParameterKey': 'Timeout',
                    'ParameterValue': str(self.args.lambda_timeout)
                }]
            layers = self.__get_lambda_layers(my_session,self.args,rule_params)


            if self.args.lambda_layers:
                additional_layers = self.args.lambda_layers.split(',')
                layers.extend(additional_layers)

            if layers:
                my_params.append({
                    'ParameterKey': 'Layers',
                    'ParameterValue': ",".join(layers)
                })

            if self.args.lambda_security_groups and self.args.lambda_subnets:
                my_params.append({
                    'ParameterKey': 'SecurityGroupIds',
                    'ParameterValue': self.args.lambda_security_groups
                })
                my_params.append({
                    'ParameterKey': 'SubnetIds',
                    'ParameterValue': self.args.lambda_subnets
                })

            #create json of CFN template
            cfn_body = os.path.join(path.dirname(__file__), 'template',  "configRuleOrganization.json")
            template_body = open(cfn_body, "r").read()
            json_body = json.loads(template_body)

            #debugging
            #print(json.dumps(json_body, indent=2))

            #deploy config rule
            my_cfn = my_session.client('cloudformation')
            try:
                my_stack_name = self.__get_stack_name_from_rule_name(rule_name)
                my_stack = my_cfn.describe_stacks(StackName=my_stack_name)
                #If we've gotten here, stack exists and we should update it.
                print ("Updating CloudFormation Stack for " + rule_name)
                try:
                    cfn_args = {
                        'StackName': my_stack_name,
                        'TemplateBody': json.dumps(json_body),
                        'Parameters': my_params,
                        'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']
                    }

                    # If no tags key is specified, or if the tags dict is empty
                    if cfn_tags is not None:
                        cfn_args['Tags'] = cfn_tags

                    response = my_cfn.update_stack(**cfn_args)
                except ClientError as e:
                    if e.response['Error']['Code'] == 'ValidationError':

                        if 'No updates are to be performed.' in str(e):
                            #No changes made to Config rule definition, so CloudFormation won't do anything.
                            print("No changes to Config Rule.")
                        else:
                            #Something unexpected has gone wrong.  Emit an error and bail.
                            print('Validation Error on CFN')
                            print(json.dumps(cfn_args))
                            print(e)
                            return 1
                    else:
                        raise

                my_lambda_arn = self.__get_lambda_arn_for_stack(my_stack_name)

                print("Publishing Lambda code...")
                my_lambda_client = my_session.client('lambda')
                my_lambda_client.update_function_code(
                    FunctionName=my_lambda_arn,
                    S3Bucket=code_bucket_name,
                    S3Key=s3_dst,
                    Publish=True
                )
                print("Lambda code updated.")
            except ClientError as e:
                #If we're in the exception, the stack does not exist and we should create it.
                print ("Creating CloudFormation Stack for " + rule_name)
                cfn_args = {
                    'StackName': my_stack_name,
                    'TemplateBody': json.dumps(json_body),
                    'Parameters': my_params,
                    'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM']
                }

                if cfn_tags is not None:
                    cfn_args['Tags'] = cfn_tags

                response = my_cfn.create_stack(**cfn_args)

            #wait for changes to propagate.
            self.__wait_for_cfn_stack(my_cfn, my_stack_name)

            #Cloudformation is not supporting tagging config rule currently.
            if cfn_tags is not None and len(cfn_tags) > 0:
                print("WARNING: Tagging is not supported for organization config rules. Only the cloudformation template will be tagged.")

        print('Config deploy complete.')

        return 0