AWSTemplateFormatVersion: "2010-09-09" Description: The template to deploy the SCP in the Master account to protect the Central backup vault. Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Lambda configuration parameters Parameters: - pLambdaFunctionName - pLambdaIAMRoleName - Label: default: Service Control Policy parameters Parameters: - pServiceControlPolicyName - pCentralBackupVaultAdmin - pCloudFormationRole - pCentralBackupVaultArn - pTargetType - pTargetId Parameters: pLambdaFunctionName: Description: The Friendly name for Lambda function. Type: String Default: ServiceControlPolicyLambda pLambdaIAMRoleName: Description: IAM role for the Lambda function. Type: String Default: ServiceControlPolicyLambdaRole pServiceControlPolicyName: Description: The Service control Policy Name. Type: String Default: CentralBackupServiceControlPolicy pCentralBackupVaultArn: Description: The arn of the Central backup vault. Type: String pCentralBackupVaultAdmin: Description: The arn of the admin for the Central backup vault. Type: String pCloudFormationRole: Description: The arn of the cloudformation StackSet execution role. Type: String pTargetType: Description: The TargetType to attach the Service Control Policy Type: String AllowedValues: - Account_Id - Organization_Unit pTargetId: Description: The Target to which Service Control Policy to be attched . This could be either Account ID or Organizational Unit. Type: String Resources: rCustomResource: Type: AWS::CloudFormation::CustomResource Properties: Description: The custom Resource to call the Service control policy Lambda. PolicyName: !Ref pServiceControlPolicyName PolicyDescription: The SCP that restricts mutative action to Central backup vault to all the Users except the Cybervault admin. PolicyContents: !Sub >- { "Version": "2012-10-17", "Statement": { "Sid": "ProtectBackupVaultOperations", "Effect": "Deny", "Action": [ "backup:PutBackupVaultAccessPolicy", "backup:DeleteVaultAccessPolicy", "backup:DeleteBackupVault", "backup:PutBackupVaultLockConfiguration" ], "Resource": "${pCentralBackupVaultArn}", "Condition": { "StringNotLike": { "aws:PrincipalArn": [ "${pCentralBackupVaultAdmin}", "${pCloudFormationRole}" ] } } } } TargetSelection: !Ref pTargetType TargetId: !Ref pTargetId ServiceToken: !GetAtt rServiceControlPolicyLambda.Arn rServiceControlPolicyLambda: Type: AWS::Lambda::Function Properties: Description: The Lambda function to Create the Service control policy and attch to the lambda. FunctionName: !Ref pLambdaFunctionName Code: ZipFile: |- import json from json import dumps import http import boto3 import logging import urllib3 #Logging configuration logging.basicConfig(level = logging.INFO) logger = logging.getLogger() logger.setLevel(logging.INFO) #Initialization service_control_policy_client = boto3.client('organizations') http = urllib3.PoolManager() #Custom resource response function def send_response(event, context, response): '''Send a response to CloudFormation to handle the custom resource lifecycle.''' responseBody = { 'Status': response, 'Reason': 'See details in CloudWatch Log Stream: ' + \ context.log_stream_name, 'PhysicalResourceId': context.log_stream_name, 'StackId': event['StackId'], 'RequestId': event['RequestId'], 'LogicalResourceId': event['LogicalResourceId'], } print('RESPONSE BODY: \n' + dumps(responseBody)) responseUrl = event['ResponseURL'] json_responseBody = json.dumps(responseBody) headers = { 'content-type' : '', 'content-length' : str(len(json_responseBody)) } try: response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody) #response = response.send(responseUrl, data=json_responseBody, headers=headers) print ("Status code: " +response.reason) except Exception as e: print ("send(..) failed executing requests.put(..): " + str(e)) return True #The function to attach SCP to the target. def attach_scp(target_type, target_resource, policy_name): try: get_scp_list = service_control_policy_client.list_policies(Filter='SERVICE_CONTROL_POLICY') for i in get_scp_list['Policies']: if i['Name'] == policy_name: get_scp_id = i['Id'] else: logger.info("The policy is not created.") if target_type == "Organization_Unit": get_targetId = service_control_policy_client.describe_organizational_unit(OrganizationalUnitId=target_resource)['OrganizationalUnit']['Id'] elif target_type == "Account_Id": get_targetId = service_control_policy_client.describe_account(AccountId=target_resource)['Account']['Id'] else: logger.info("Enter the correct Target type") scp_attach = service_control_policy_client.attach_policy(PolicyId = get_scp_id,TargetId = get_targetId) logger.info(f"The Created Service control policy was attached to the target {target_resource}") return True except Exception as e: logger.info(f"The Created Service control policy failed to the target {target_resource} because of {e}") return False #The function to update the target if required during during update stack. def update_target(target_type, target_resource, get_scp_id): try: get_current_target = service_control_policy_client.list_targets_for_policy(PolicyId=get_scp_id) for i in get_current_target['Targets']: existing_target_id = i['TargetId'] if target_type == "Organization_Unit": get_targetId = service_control_policy_client.describe_organizational_unit(OrganizationalUnitId=target_resource)['OrganizationalUnit']['Id'] if get_targetId == existing_target_id: return True else: logger.info(f"Detaching the Target Organization Unit {existing_target_id} from the policy.") detach_scp = service_control_policy_client.detach_policy(PolicyId= get_scp_id, TargetId = existing_target_id) logger.info(f"Attaching the policy to the Target Organization Unit {get_targetId}.") scp_attach = service_control_policy_client.attach_policy(PolicyId = get_scp_id,TargetId = get_targetId) return True if target_type == "Account_Id": get_targetId = service_control_policy_client.describe_account(AccountId=target_resource)['Account']['Id'] if get_targetId == existing_target_id: return True else: logger.info(f"Detaching the Target account {existing_target_id} from the policy.") detach_scp = service_control_policy_client.detach_policy(PolicyId= get_scp_id, TargetId = existing_target_id) logger.info(f"Attaching the policy to the Target account {get_targetId}.") scp_attach = service_control_policy_client.attach_policy(PolicyId = get_scp_id,TargetId = get_targetId) return True else: logger.info("Enter the correct Target type") return False except Exception as e: logger.info(f"The Service control policy was updated but failed to attach to the target{target_resource} because of {e}") return False #The function to detach and delete SCP when delete stack is called. def delete_scp(policy_name, target_type, target_resource): try: if target_type == "Organization_Unit": get_targetId = service_control_policy_client.describe_organizational_unit(OrganizationalUnitId=target_resource)['OrganizationalUnit']['Id'] elif target_type == "Account_Id": get_targetId = service_control_policy_client.describe_account(AccountId=target_resource)['Account']['Id'] else: logger.info("Enter the correct Target type") get_scp_list = service_control_policy_client.list_policies(Filter='SERVICE_CONTROL_POLICY') for i in get_scp_list['Policies']: if i['Name'] == policy_name: get_scp_id = i['Id'] logger.info("Detaching the Target from the policy.") detach_scp = service_control_policy_client.detach_policy(PolicyId= get_scp_id, TargetId = get_targetId) logger.info("DeletingService control policy") delete_scp = service_control_policy_client.delete_policy(PolicyId = get_scp_id) return True except Exception as e: logger.info(f"The Created Service control policy failed to the target {target_resource} because of {e}") return False #The main Lambda function. def lambda_handler(event, context): logger.info(event) policy_name = event['ResourceProperties']['PolicyName'] policy_contents = event['ResourceProperties']['PolicyContents'] policy_description = event['ResourceProperties']['PolicyDescription'] target_type = event['ResourceProperties']['TargetSelection'] target_resource = event['ResourceProperties']['TargetId'] if event['RequestType'] == 'Create': try: logger.info("Creating Service control policy") create_scp = service_control_policy_client.create_policy(Content = policy_contents, Description = policy_description, Name = policy_name, Type='SERVICE_CONTROL_POLICY') logger.info("The Service control policy created successfully") Attach_scp = attach_scp(target_type, target_resource, policy_name) if Attach_scp: response = 'SUCCESS' else: response = 'FAILED' except Exception as e: logger.info(f"Creating Service control policy failed because of {e}") response = 'FAILED' send_response(event, context, response) if event['RequestType'] == 'Update': try: get_scp_list = service_control_policy_client.list_policies(Filter='SERVICE_CONTROL_POLICY') for i in get_scp_list['Policies']: if i['Name'] == policy_name: get_scp_id = i['Id'] logger.info("Updating Service control policy") update_scp = service_control_policy_client.update_policy(PolicyId = get_scp_id, Content = policy_contents, Description = policy_description, Name = policy_name) target_scp = update_target(target_type, target_resource, get_scp_id) if target_scp: response = 'SUCCESS' else: response = 'FAILED' except Exception as e: logger.info(f"Updating Service control policy failed because of {e}") response = 'FAILED' send_response(event, context, response) if event['RequestType'] == 'Delete': try: Delete_scp = delete_scp(policy_name, target_type, target_resource) if Delete_scp: response = 'SUCCESS' else: response = 'FAILED' except Exception as e: logger.info(f"Deletion of Service control policy failed because of {e}") response = 'FAILED' send_response(event, context, response) Handler: index.lambda_handler MemorySize: 128 Role: !GetAtt rServiceControlPolicyLambdaRole.Arn Runtime: python3.8 Timeout: 180 rServiceControlPolicyLambdaRole: Type: AWS::IAM::Role Properties: RoleName: !Ref pLambdaIAMRoleName AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Policies: - PolicyName: Cloudwatch-Lambda-Policy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:*:*:* rServiceControlPolicyLambdaPolicy: Type: AWS::IAM::Policy Properties: Roles: - !Ref rServiceControlPolicyLambdaRole PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - organizations:CreatePolicy - organizations:UpdatePolicy - organizations:AttachPolicy - organizations:DetachPolicy - organizations:DeletePolicy - organizations:DescribeOrganizationalUnit - organizations:DescribeAccount - organizations:ListPolicies - organizations:ListTargetsForPolicy Resource: '*' PolicyName: PolicyToCreateSCP