Enable Amazon GuardDuty conditionally by using AWS CloudFormation templates - AWS Prescriptive Guidance

Enable Amazon GuardDuty conditionally by using AWS CloudFormation templates

Created by Ram Kandaswamy (AWS)

Environment: Production

Technologies: Security, identity, compliance; DevOps; Operations

AWS services: AWS CloudFormation; Amazon GuardDuty; AWS Lambda; AWS Identity and Access Management

Summary

You can enable Amazon GuardDuty on an Amazon Web Services (AWS) account by using an AWS CloudFormation template. By default, if GuardDuty is already enabled when you try to use CloudFormation to turn it on, the stack deployment fails. However, you can use conditions in your CloudFormation template to check whether GuardDuty is already enabled. CloudFormation supports the use of conditions that compare static values; it does not support using the output of another resource property within the same template. For more information, see Conditions in the CloudFormation user guide.

In this pattern, you use a CloudFormation custom resource backed by an AWS Lambda function to conditionally enable GuardDuty if it is not already enabled. If GuardDuty is enabled, the stack captures the status and records it in the output section of the stack. If GuardDuty is not enabled, the stack enables it.

Prerequisites and limitations

Prerequisites

  • An active AWS account

  • An AWS Identity and Access Management (IAM) role that has permissions to create, update, and delete CloudFormation stacks

Limitations

  • If GuardDuty has been manually disabled for an AWS account or Region, this pattern does not enable GuardDuty for that target account or Region.

Architecture

Target technology stack

The pattern uses CloudFormation for Infrastructure as Code (IaC). You use a CloudFormation custom resource backed by a Lambda function to achieve the dynamic service-enablement capability.

Target architecture

The following high-level architecture diagram shows the process of enabling GuardDuty by deploying a CloudFormation template:

  1. You deploy a CloudFormation template to create a CloudFormation stack.

  2. The stack creates an IAM role and a Lambda function.

  3. The Lambda function assumes the IAM role.

  4. If GuardDuty is not already enabled on the target AWS account, the Lambda function enables it.

The process of enabling GuardDuty by deploying a CloudFormation template

Automation and scale

You can use the AWS CloudFormation StackSet feature to extend this solution to multiple AWS accounts and AWS Regions. For more information, see Working with AWS CloudFormation StackSets in the CloudFormation user guide.

Tools

  • AWS Command Line Interface (AWS CLI) is an open-source tool that helps you interact with AWS services through commands in your command-line shell.

  • AWS CloudFormation helps you set up AWS resources, provision them quickly and consistently, and manage them throughout their lifecycle across AWS accounts and Regions.

  • Amazon GuardDuty is a continuous security monitoring service that analyzes and processes logs to identify unexpected and potentially unauthorized activity in your AWS environment.

  • AWS Identity and Access Management (IAM) helps you securely manage access to your AWS resources by controlling who is authenticated and authorized to use them.

  • AWS Lambda is a compute service that helps you run code without needing to provision or manage servers. It runs your code only when needed and scales automatically, so you pay only for the compute time that you use.

Epics

TaskDescriptionSkills required

Create the CloudFormation template.

  1. Copy the code in CloudFormation template in the Additional information section.

  2. Paste the code in a text editor.

  3. Save the file as sample.yaml on your workstation.

AWS DevOps

Create the CloudFormation stack.

  1. In AWS CLI, enter the following command. This creates a new CloudFormation stack using the sample.yaml file. For more information, see Creating a stack in the CloudFormation user guide.

    aws cloudformation create-stack \ --stack-name guardduty-cf-stack \ --template-body file://sample.yaml
  2. Confirm the following value appears in the AWS CLI, indicating that the stack has been successfully created. The amount of time required to create the stack can vary.

    "StackStatus": "CREATE_COMPLETE",
AWS DevOps

Validate that GuardDuty is enabled for the AWS account.

  1. Sign in to the AWS Management Console and open the GuardDuty console at https://console.aws.amazon.com/guardduty/.

  2. Verify that the GuardDuty service is enabled.

Cloud administrator, AWS administrator

Configure additional accounts or AWS Regions.

As needed for your use case, use the AWS CloudFormation StackSet feature to extend this solution to multiple AWS accounts and AWS Regions. For more information, see Working with AWS CloudFormation StackSets in the CloudFormation user guide.

Cloud administrator, AWS administrator

Related resources

References

Tutorials and videos

Additional information

CloudFormation template

AWSTemplateFormatVersion: 2010-09-09 Resources: rLambdaLogGroup: Type: 'AWS::Logs::LogGroup' DeletionPolicy: Delete Properties: RetentionInDays: 7 LogGroupName: /aws/lambda/resource-checker rLambdaCheckerLambdaRole: Type: 'AWS::IAM::Role' Properties: RoleName: !Sub 'resource-checker-lambda-role-${AWS::Region}' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: 'sts:AssumeRole' Path: / Policies: - PolicyName: !Sub 'resource-checker-lambda-policy-${AWS::Region}' PolicyDocument: Version: 2012-10-17 Statement: - Sid: CreateLogGroup Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'iam:CreateServiceLinkedRole' - 'cloudformation:CreateStack' - 'cloudformation:DeleteStack' - 'cloudformation:Desc*' - 'guardduty:CreateDetector' - 'guardduty:ListDetectors' - 'guardduty:DeleteDetector' Resource: '*' resourceCheckerLambda: Type: 'AWS::Lambda::Function' Properties: Description: Checks for resource type enabled and possibly name to exist FunctionName: resource-checker Handler: index.lambda_handler Role: !GetAtt - rLambdaCheckerLambdaRole - Arn Runtime: python3.8 MemorySize: 128 Timeout: 180 Code: ZipFile: | import boto3 import os import json from botocore.exceptions import ClientError import cfnresponse guardduty=boto3.client('guardduty') cfn=boto3.client('cloudformation') def lambda_handler(event, context): print('Event: ', event) if 'RequestType' in event: if event['RequestType'] in ["Create","Update"]: enabled=False try: response=guardduty.list_detectors() if "DetectorIds" in response and len(response["DetectorIds"])>0: enabled="AlreadyEnabled" elif "DetectorIds" in response and len(response["DetectorIds"])==0: cfn_response=cfn.create_stack( StackName='guardduty-cfn-stack', TemplateBody='{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "A sample template", "Resources": { "IRWorkshopGuardDutyDetector": { "Type": "AWS::GuardDuty::Detector", "Properties": { "Enable": true } } } }' ) enabled="True" except Exception as e: print("Exception: ",e) responseData = {} responseData['status'] = enabled cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID" ) elif event['RequestType'] == "Delete": cfn_response=cfn.delete_stack( StackName='guardduty-cfn-stack') cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) CheckResourceExist: Type: 'Custom::LambdaCustomResource' Properties: ServiceToken: !GetAtt - resourceCheckerLambda - Arn Outputs: status: Value: !GetAtt - CheckResourceExist - status

Alternative code option for the Lambda resource

The provided CloudFormation template uses inline code to reference the Lambda resource, for easier reference and guidance. Alternatively, you can place the Lambda code in an Amazon Simple Storage Service (Amazon S3) bucket and reference it in the CloudFormation template. Inline code doesn’t support package dependencies or libraries. You can support these by placing the Lambda code in an S3 bucket and referencing it in the CloudFormation template.

Replace the following lines of code:

Code: ZipFile: |

with the following lines of code:

Code: S3Bucket: <bucket name> S3Key: <python file name> S3ObjectVersion: <version>

The S3ObjectVersion property can be omitted if you are not using versioning in your S3 bucket. For more information, see Using versioning in S3 buckets in the Amazon S3 user guide.