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:
You deploy a CloudFormation template to create a CloudFormation stack.
The stack creates an IAM role and a Lambda function.
The Lambda function assumes the IAM role.
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](/images/prescriptive-guidance/latest/patterns/images/pattern-img/3abd7cb7-0937-41fe-8eaa-79aedb182732/images/b2d81a61-31f7-4cb9-9de3-f7db1b92ccd9.png)
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
Task | Description | Skills required |
---|---|---|
Create the CloudFormation template. |
| AWS DevOps |
Create the CloudFormation stack. |
| AWS DevOps |
Validate that GuardDuty is enabled for the AWS account. |
| 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
Simplify Your Infrastructure Management Using AWS CloudFormation
(Tutorial) Use Amazon GuardDuty and AWS Security Hub to secure multiple accounts
(AWS re:Invent 2020) Best practices for authoring AWS CloudFormation
(AWS re:Invent 2019) Threat Detection on AWS: An Introduction to Amazon GuardDuty
(AWS re:Inforce 2019)
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.