cdk-nag
Check CDK applications or CloudFormation templates for best practices using a combination of available rule packs. Inspired by cfn_nag.
Check out this blog post for a guided overview!
Available Rules and Packs
See RULES for more information on all the available packs.
RULES also includes a collection of additional rules that are not currently included in any of the pre-built NagPacks, but are still available for inclusion in custom NagPacks.
Read the NagPack developer docs if you are interested in creating your own pack.
Usage
For a full list of options See NagPackProps in the API.md
Including in an application
from aws_cdk import App, Validations
from cdk_nag import AwsSolutionsChecks, NIST80053R5Checks
# CdkTestStack: Any
app = App()
CdkTestStack(app, "CdkNagDemo")
# Simple rule informational messages using the AWS Solutions Rule pack
Validations.of(app).add_plugins(AwsSolutionsChecks(app))
# Multiple rule packs can be run against the same app
Validations.of(app).add_plugins(NIST80053R5Checks(app))
Acknowledging a Rule
Use CDK's native Validations.of() API to acknowledge (suppress) rule violations on specific constructs.
Example 1) Acknowledging a rule on a construct
from aws_cdk.aws_ec2 import SecurityGroup, Vpc, Peer, Port
from aws_cdk import Stack, StackProps, Validations
from constructs import Construct
class CdkTestStack(Stack):
def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)
test = SecurityGroup(self, "test",
vpc=Vpc(self, "vpc")
)
test.add_ingress_rule(Peer.any_ipv4(), Port.all_traffic())
Validations.of(test).acknowledge(
id="AwsSolutions-EC23",
reason="This security group is used for internal testing only."
)
Example 2) Acknowledging a rule on a stack
from aws_cdk import App, Validations
from cdk_nag import AwsSolutionsChecks
# CdkTestStack: Any
app = App()
stack = CdkTestStack(app, "CdkNagDemo")
Validations.of(app).add_plugins(AwsSolutionsChecks(app))
Validations.of(stack).acknowledge(
id="AwsSolutions-EC23",
reason="All security groups in this stack are internal only."
)
Example 3) Acknowledging a specific finding
Certain rules report multiple findings per resource (e.g., IAM wildcard permissions). Each finding has its own ID in the format RuleId[FindingId].
If you received the following errors on synth/deploy:
[Error at /StackName/rUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Action::s3:*]: The IAM entity contains wildcard permissions.
[Error at /StackName/rUser/DefaultPolicy/Resource] AwsSolutions-IAM5[Resource::*]: The IAM entity contains wildcard permissions.
You can acknowledge a specific finding:
from aws_cdk.aws_iam import User, PolicyStatement
from aws_cdk import Stack, StackProps, Validations
from constructs import Construct
class CdkTestStack(Stack):
def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)
user = User(self, "rUser")
user.add_to_policy(
PolicyStatement(
actions=["s3:*"],
resources=["*"]
))
# Only acknowledge the s3:* action — Resource::* still triggers
Validations.of(user).acknowledge(
id="AwsSolutions-IAM5[Action::s3:*]",
reason="Need s3:* for cross-account replication."
)
Rules and Property Overrides
In some cases L2 Constructs do not have a native option to remediate an issue and must be fixed via Raw Overrides. Since raw overrides take place after template synthesis these fixes are not caught by cdk-nag. In this case you should remediate the issue and acknowledge the rule.
Example) Property Overrides
from aws_cdk.aws_ec2 import Instance, InstanceType, InstanceClass, MachineImage, Vpc, CfnInstance
from aws_cdk import Stack, StackProps, Validations
from constructs import Construct
class CdkTestStack(Stack):
def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)
instance = Instance(self, "rInstance",
vpc=Vpc(self, "rVpc"),
instance_type=InstanceType(InstanceClass.T3),
machine_image=MachineImage.latest_amazon_linux()
)
cfn_ins = instance.node.default_child
cfn_ins.add_property_override("DisableApiTermination", True)
Validations.of(instance).acknowledge(
id="AwsSolutions-EC29",
reason="Remediated through property override."
)
Audit Trail: CloudFormation Metadata
By default, cdk-nag writes violations to CDK's policy-validation-report.json in the cloud assembly. If you need the v2-compatible cdk_nag metadata block in your synthesized CloudFormation templates (for existing compliance tooling), enable writeSuppressionsToCloudFormation:
from aws_cdk import App, Validations
from cdk_nag import AwsSolutionsChecks
app = App()
# Writes acknowledged rules into CfnResource Metadata as cdk_nag: { rules_to_suppress: [...] }
Validations.of(app).add_plugins(AwsSolutionsChecks(app, write_suppressions_to_cloud_formation=True))
This registers a WriteNagSuppressionsToCloudFormationAspect that runs during synthesis and copies Validations.of().acknowledge() data into the CloudFormation template Metadata section, preserving the same format as cdk-nag v2.
Using on CloudFormation templates
You can use cdk-nag on existing CloudFormation templates by using the cloudformation-include module.
Example) CloudFormation template
Sample App
from aws_cdk import App, Validations
from cdk_nag import AwsSolutionsChecks
# CdkTestStack: Any
app = App()
CdkTestStack(app, "CdkNagDemo")
Validations.of(app).add_plugins(AwsSolutionsChecks(app))
Sample Stack with imported template
from aws_cdk.cloudformation_include import CfnInclude
from aws_cdk import Stack, StackProps, Validations
from constructs import Construct
class CdkTestStack(Stack):
def __init__(self, scope, id, *, description=None, env=None, stackName=None, tags=None, notificationArns=None, synthesizer=None, terminationProtection=None, analyticsReporting=None, crossRegionReferences=None, permissionsBoundary=None, suppressTemplateIndentation=None, propertyInjectors=None):
super().__init__(scope, id, description=description, env=env, stackName=stackName, tags=tags, notificationArns=notificationArns, synthesizer=synthesizer, terminationProtection=terminationProtection, analyticsReporting=analyticsReporting, crossRegionReferences=crossRegionReferences, permissionsBoundary=permissionsBoundary, suppressTemplateIndentation=suppressTemplateIndentation, propertyInjectors=propertyInjectors)
template = CfnInclude(self, "Template",
template_file="my-template.json"
)
# Acknowledge rules on imported resources
bucket = template.get_resource("rBucket")
Validations.of(bucket).acknowledge(
id="AwsSolutions-S1",
reason="Logging not required for this bucket."
)
Migrating from v2
cdk-nag v3 replaces the custom NagSuppressions API with CDK's native Validations.of().acknowledge() mechanism.
| v2 | v3 |
|---|---|
NagSuppressions.addResourceSuppressions(construct, [{ id, reason }]) |
Validations.of(construct).acknowledge({ id, reason }) |
NagSuppressions.addStackSuppressions(stack, [{ id, reason }]) |
Validations.of(stack).acknowledge({ id, reason }) |
NagSuppressions.addResourceSuppressionsByPath(stack, path, [...]) |
Validations.of(construct).acknowledge({ id, reason }) |
appliesTo: ['Action::s3:*'] |
id: 'AwsSolutions-IAM5[Action::s3:*]' |
{ id: 'CdkNagValidationFailure', reason: '...' } |
Validations.of(construct).acknowledge({ id: 'RuleId', reason: '...' }) |
Note on bulk suppression: In v2, suppressing a rule without appliesTo would suppress all findings for that rule on the construct. In v3, each finding must be acknowledged individually (e.g., AwsSolutions-IAM5[Action::s3:*] and AwsSolutions-IAM5[Resource::*] are separate acknowledgments). Prefix matching (acknowledging AwsSolutions-IAM5 to suppress all findings) is not yet supported — tracked via [issue link].
Removed APIs:
NagSuppressions(useValidations.of().acknowledge())INagSuppressionIgnoreand all condition classesNagPackSuppressioninterfaceCdkNagValidationFailureconceptlogIgnoresandsuppressionIgnoreConditionprops
Contributing
See CONTRIBUTING for more information.
License
This project is licensed under the Apache-2.0 License.
Release files for cdk-nag 3.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cdk_nag-3.0.2.tar.gz | 735.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cdk_nag-3.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.5 MB
Release files / cdk_nag-3.0.2.tar.gz
| Download URL | cdk_nag-3.0.2.tar.gz |
|---|---|
| Size | 735.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c34f8128ac28368b34cb5d77ba21a5d541e652596d8ea844f608c0b76dc91f0d
|
|
BLAKE2b-256 checksum How to use checksums |
afa0bc4cb0b639c56ba1f5459ecbf2540597ee5af1afd7b79632548e165f6d14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.14.6
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logRelease files / cdk_nag-3.0.2-py3-none-any.whl
| Download URL | cdk_nag-3.0.2-py3-none-any.whl |
|---|---|
| Size | 732.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
861d8d30f5eb062d23462013bd6719fa355428f5525208b61f50da813065ea71
|
|
BLAKE2b-256 checksum How to use checksums |
7e2f5a0e2b20eccd6afb31f8214ddbcd981bac2ea624281d133cbb914235fa16
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.14.6
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency log