in source/repositories/compliant-framework-central-pipeline/lambda/create_update_stack/index.py [0:0]
def lambda_handler(event, context):
# pylint: disable=E1101
cp_client = boto3.client('codepipeline')
try:
print(f'event:')
print(json.dumps(event))
# Extract the Job ID
job_id = event['CodePipeline.job']['id']
# Extract the Job Data
job_data = event['CodePipeline.job']['data']
ssm_client = boto3.client('ssm')
params = json.loads(
job_data['actionConfiguration']['configuration']['UserParameters'])
print(f'params:')
print(json.dumps(params))
# Get the current account ID
sts_client = boto3.client('sts')
current_account_id = sts_client.get_caller_identity()['Account']
print(f'current_account_id: {current_account_id}')
# Get the partition
if ('gov' in os.environ['AWS_REGION']):
partition = 'aws-us-gov'
else:
partition = 'aws'
print(f'partition: {partition}')
account_id = params['account']
region = params['region']
if current_account_id == account_id:
cfn_client = boto3.client('cloudformation',
region_name=region)
else:
# Assume Role
role_arn = f'arn:{partition}:iam::{account_id}:role/CompliantFrameworkAccountAccessRole'
assumed_role = sts_client.assume_role(
RoleArn=role_arn,
RoleSessionName='CompliantFramework'
)
cfn_client = boto3.client(
'cloudformation',
aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
aws_session_token=assumed_role['Credentials']['SessionToken'],
region_name=region
)
if 'continuationToken' in job_data:
continuation_token = json.loads(job_data['continuationToken'])
stack_id = continuation_token['stack_id']
print(f'stack_id: {stack_id}')
stack_name = continuation_token['stack_name']
print(f'stack_name: {stack_name}')
response = cfn_client.describe_stacks(StackName=stack_name)
print('describe_stacks:')
print(json.dumps(response, default=str))
for stack in response['Stacks']:
if (stack['StackId'] == stack_id):
stack_status = stack['StackStatus']
# Succeeded - Done.
if stack_status in [
'CREATE_COMPLETE',
'UPDATE_COMPLETE']:
# Create Outputs here
outputVariables = {}
if ('Outputs' in stack):
for output in stack['Outputs']:
key = output['OutputKey']
value = output['OutputValue']
outputVariables[key] = value
cp_client.put_job_success_result(
jobId=job_id,
outputVariables=outputVariables
)
# Failed - Done.
elif stack_status in [
'UPDATE_ROLLBACK_COMPLETE',
'ROLLBACK_COMPLETE',
'CREATE_FAILED',
'ROLLBACK_FAILED',
'DELETE_FAILED',
'UPDATE_ROLLBACK_FAILED']:
cp_client.put_job_failure_result(
jobId=job_id,
failureDetails={
'message': f'Stack Status: {stack_status}',
'type': 'JobFailed'
}
)
# Still Running - Continue
else:
cp_client.put_job_success_result(
jobId=job_id,
continuationToken=json.dumps({
'stack_id': stack_id,
'stack_name': stack_name
})
)
else:
parameter_dict = {}
if 'parameterOverrides' in params:
for key in params['parameterOverrides']:
parameter_dict[key] = params['parameterOverrides'][key]
if 'ssmParameterPath' in params:
ssm_params = json.loads(ssm_client.get_parameter(
Name=params['ssmParameterPath']
)['Parameter']['Value'])
for key in ssm_params:
if not key in parameter_dict:
parameter_dict[key] = ssm_params[key]
parameters = []
for key in parameter_dict:
value = parameter_dict[key]
if isinstance(value, str):
parameters.append(
{
'ParameterKey': key,
'ParameterValue': value
}
)
elif isinstance(value, dict):
parameters.append(
{
'ParameterKey': key,
'ParameterValue': json.dumps(value)
}
)
print('parameters:')
print(json.dumps(parameters, default=str))
capabilities = []
if 'capabilities' in params:
capabilities.append(params['capabilities'])
print('capabilities:')
print(capabilities)
bucket_domain = params['bucketRegionalDomainName']
prefix = params['templatePrefix']
path = params['templatePath']
template_url = f'https://{bucket_domain}/{prefix}/{path}'
print(f'template_url: {template_url}')
stack_name = params['stackName']
stack_exists = False
try:
# If the stack does not exist, an AmazonCloudFormationException
# is returned.
response = cfn_client.describe_stacks(StackName=stack_name)
print(json.dumps(response, default=str))
stack_exists = True
for stack in response['Stacks']:
if (stack['StackName'] == stack_name):
stack_status = stack['StackStatus']
if stack_status == 'ROLLBACK_COMPLETE':
response = cfn_client.delete_stack(
StackName=stack_name
)
print('delete_stack response:')
print(json.dumps(response, default=str))
waiter = cfn_client.get_waiter(
'stack_delete_complete')
waiter.wait(StackName=stack_name, WaiterConfig={
'Delay': 30, 'MaxAttempts': 20})
stack_exists = False
break
except:
pass
if stack_exists:
print('Stack exists - updating stack')
response = cfn_client.update_stack(
StackName=stack_name,
TemplateURL=template_url,
Parameters=parameters,
Capabilities=capabilities
)
else:
print('Stack does not exists - creating stack')
response = cfn_client.create_stack(
StackName=stack_name,
TemplateURL=template_url,
Parameters=parameters,
Capabilities=capabilities
)
print(json.dumps(response, default='str'))
stack_id = response['StackId']
print(f'stack_id: {stack_id}')
cp_client.put_job_success_result(
jobId=job_id,
continuationToken=json.dumps({
'stack_id': stack_id,
'stack_name': stack_name
})
)
except Exception as e:
# If any other exceptions which we didn't expect are raised
# then fail the job and log the exception message.
print('Function failed due to exception.')
print(e)
cp_client.put_job_failure_result(
jobId=job_id, failureDetails={
'message': e,
'type': 'JobFailed'
}
)