Enable Amazon GuardDuty conditionally by using AWS CloudFormation templates
Ram Kandaswamy, Amazon Web Services
Summary
AWS CloudFormation, an infrastructure as code (IaC) tool, helps you to manage AWS resources through template-based deployments. CloudFormation is typically used to manage AWS resources. Using it to enable AWS services, such as Amazon GuardDuty, can present unique challenges. GuardDuty is a threat detection service that continuously monitors your AWS accounts for malicious activity and unauthorized behavior. Unlike typical resources that can be created multiple times, GuardDuty is a service that needs to be enabled once per account and AWS Region. Traditional CloudFormation conditions support only static value comparisons, which makes it difficult to check the current state of services such as GuardDuty. If you attempt to enable GuardDuty through CloudFormation in an account where it's already active, the stack deployment fails. This can create operational challenges for DevOps teams that are managing multi-account environments.
This pattern introduces a solution to this challenge. It uses CloudFormation custom resources that are backed by AWS Lambda functions to perform dynamic state checks. The conditional logic enables GuardDuty only if it isn’t already enabled. It uses the stack outputs to record the GuardDuty status for future reference.
By following this pattern, you can automate GuardDuty deployments across your AWS infrastructure while maintaining clean, predictable CloudFormation stack operations. This approach is particularly valuable for organizations that are:
Managing multiple AWS accounts through IaC
Implementing security services at scale
Requiring idempotent infrastructure deployments
Automating security service deployments
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
AWS Command Line Interface (AWS CLI), installed and configured
Limitations
If GuardDuty has been manually disabled for an AWS account or AWS 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.
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 documentation.
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 |
|---|---|---|
Store the code in Amazon S3. |
| AWS DevOps |
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 Regions. | As needed for your use case, use the 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 documentation. | 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 CSPM 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
Python code
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": "Guard duty creation 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, {})