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