Last active
May 4, 2021 06:44
-
-
Save adamcousins/1849a603660b9b6bb9939cd8ad37cb14 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: '2010-09-09' | |
Description: "Security: Delegate an account for AWS SecurityHub centralisation within a single region within an AWS Organisation." | |
Parameters: | |
ServicePrincipal: | |
Type: String | |
Description: The Service Principal to delegate access to | |
Default: securityhub.amazonaws.com | |
AdminAccountId: | |
Type: String | |
Description: Delegated AWS Account Id to manage Security Hub across an AWS Organisation | |
LogRetention: | |
Type: String | |
Description: Log retention in number of days | |
Default: '7' | |
Resources: | |
### Cloudformation Custom Resource ### | |
### AWS Security Hub - Delegates an AWS Account as the AWS Security Hub Admin Account. ### | |
### Required Properties: | |
### ServiceToken: [Type: String] | |
### AdminAccountId: [Type: String] | |
### ServicePrincipal: [Type: String] | |
### Attributes (Output): | |
### None | |
### Notes: | |
### Defining this resource delegates an AWS Account as the AWS Security Hub Admin Account. | |
### It is expected this stack to be launched within the AWS Organisation Master Account | |
### This resource will also enable the securityhub principal in an AWS Organisation | |
### Example Usage ### | |
# EnableSecurityHubAdminAccount: | |
# Type: Custom::EnableSecurityHubAdminAccount | |
# Properties: | |
# ServiceToken: !GetAtt Function.Arn | |
# AdminAccountId: !Ref AdminAccountId | |
# ServicePrincipal: !Ref ServicePrincipal | |
### End Cloudformation Custom Resource ### | |
FunctionLogGroup: | |
Type: "AWS::Logs::LogGroup" | |
DeletionPolicy: Retain | |
Properties: | |
RetentionInDays: !Ref LogRetention | |
LogGroupName: !Sub "/aws/lambda/${Function}" | |
Function: | |
Type: AWS::Lambda::Function | |
Properties: | |
Description: Delegate an account for AWS Security Hub centralisation within a single region within an AWS Organisation | |
Handler: index.lambda_handler | |
Code: | |
ZipFile: | | |
import os, logging, boto3 | |
from botocore.exceptions import ClientError | |
import cfnresponse | |
log = logging.getLogger() | |
log.setLevel(logging.INFO) | |
client = boto3.client('securityhub') | |
org_client = boto3.client('organizations') | |
region = os.environ['AWS_REGION'] | |
def enable_service(service_principal): | |
log.info('enabling ' + service_principal + ' service in AWS Organizations') | |
response = org_client.enable_aws_service_access( | |
ServicePrincipal=service_principal | |
) | |
return response | |
def disable_service(service_principal): | |
log.info('disabling ' + service_principal + ' service in AWS Organizations') | |
response = org_client.disable_aws_service_access( | |
ServicePrincipal=service_principal | |
) | |
return response | |
def create_admin(admin_account_id): | |
log.info('enabling account id: ' + admin_account_id + ' as the master account in AWS region: ' + region) | |
client.enable_organization_admin_account( | |
AdminAccountId=admin_account_id | |
) | |
return admin_account_id | |
def delete_admin(admin_account_id): | |
log.info('disabling account id: ' + admin_account_id + ' as the master account in AWS region: ' + region) | |
client.disable_organization_admin_account( | |
AdminAccountId=admin_account_id | |
) | |
return admin_account_id | |
def lambda_handler(event, context): | |
log.info('REQUEST RECEIVED:\n %s', event) | |
admin_account_id = event['ResourceProperties']['AdminAccountId'] | |
service_principal = event['ResourceProperties']['ServicePrincipal'] | |
return_msg = {} | |
return_status = "FAILED" | |
physicalResourceId = admin_account_id | |
try: | |
if(event['RequestType'] == 'Create'): | |
enable_service(service_principal) | |
physicalResourceId = create_admin(admin_account_id) | |
elif(event['RequestType'] == 'Update'): | |
delete_admin(physicalResourceId) | |
physicalResourceId = create_admin(admin_account_id) | |
else: | |
delete_admin(physicalResourceId) | |
#wont disable service upon deletion as it fails with registered admins even though there is none | |
#disable_service(service_principal) | |
return_msg = {"Message": "master account id: " + admin_account_id + " has been " + event['RequestType'] + "d"} | |
return_status = "SUCCESS" | |
except ClientError as e: | |
log.info(e.response['Error']['Message']) | |
return_msg = {"Message": "Exception Found: " + e.response['Error']['Message']} | |
cfnresponse.send(event, context, return_status, return_msg, physicalResourceId) | |
Runtime: python3.6 | |
Role: !GetAtt FunctionRole.Arn | |
Timeout: 120 | |
FunctionRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: | |
- sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Version: '2012-10-17' | |
Path: "/" | |
Policies: | |
- PolicyName: root | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Resource: "*" | |
Effect: Allow | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
- Resource: '*' | |
Effect: Allow | |
Action: | |
- securityhub:EnableOrganizationAdminAccount | |
- securityhub:DisableOrganizationAdminAccount | |
- organizations:RegisterDelegatedAdministrator | |
- organizations:DeregisterDelegatedAdministrator | |
- organizations:DescribeOrganization | |
- organizations:ListDelegatedServicesForAccount | |
- organizations:ListDelegatedAdministrators | |
- organizations:ListAWSServiceAccessForOrganization | |
- Resource: '*' | |
Effect: Allow | |
Action: | |
- organizations:EnableAWSServiceAccess | |
- organizations:DisableAWSServiceAccess | |
Condition: | |
StringLikeIfExists: | |
organizations:ServicePrincipal: !Ref ServicePrincipal | |
# Usage ### | |
EnableSecurityHubAdminAccount: | |
DependsOn: FunctionLogGroup | |
Type: Custom::EnableSecurityHubAdminAccount | |
Properties: | |
ServiceToken: !GetAtt Function.Arn | |
AdminAccountId: !Ref AdminAccountId | |
ServicePrincipal: !Ref ServicePrincipal | |
Outputs: | |
FunctionArn: | |
Description: The Lambda Function Arn | |
Value: !GetAtt Function.Arn | |
AdminAccountId: | |
Description: The SecurityHub Admin Account Id | |
Value: !Ref AdminAccountId | |
ServicePrincipal: | |
Description: The Service Principal to delegate access to | |
Value: !Ref ServicePrincipal |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment