Skip to content

Commit 2ec791f

Browse files
committed
initial commit of tagging governance
1 parent 18c5b40 commit 2ec791f

11 files changed

+1188
-0
lines changed

Tagging-Governance/README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Tagging Governance using AWS Organizations
2+
A solution for implementing enterprise-grade resource tagging governance using AWS Organizations Service Control Policies (SCPs).
3+
4+
## Overview
5+
This project demonstrates how to implement preventative tagging controls for AWS resources using Service Control Policies. It leverages De Morgan's Laws to create complex logical conditions within SCP constraints, ensuring consistent resource tagging across multiple AWS accounts.
6+
7+
## Features
8+
Enforce required tags at resource creation time
9+
Scale across multiple AWS accounts
10+
Support for complex tag validation patterns
11+
Integration with AWS Backup based on compliance tags
12+
Support for hybrid and edge deployment patterns
13+
Architecture
14+
The solution implements tagging governance through:
15+
16+
## Service Control Policies (SCPs) for tag enforcement
17+
AWS Organizations for policy management
18+
AWS Lambda for testing and validation
19+
AWS Backup for automated backup policies
20+
21+
## Important Notes on SCP Implementation
22+
23+
### ⚠️ Testing and Deployment Strategy
24+
25+
The service control policies in this repository are provided as examples only. Before implementing any SCPs:
26+
27+
1. **Thoroughly test** all policies before attachment
28+
2. Test in an isolated environment that mirrors your production setup
29+
3. Use a gradual deployment approach:
30+
- Start with specific, limited-scope OUs
31+
- Gradually expand to broader OUs
32+
- Monitor and verify impact at each stage
33+
34+
### Understanding SCP Behavior
35+
36+
- SCPs provide coarse-grained guardrails but **do not grant permissions**
37+
- Administrators must still manage identity-based and resource-based policies
38+
- Effective permissions are the **logical intersection** of:
39+
- Service Control Policy/Resource Control Policy
40+
- Identity Policy or Resource Policy
41+
42+
### Organizational Design Recommendations
43+
44+
- Organize accounts by function, compliance requirements, or control sets
45+
- Avoid mirroring organizational reporting structure
46+
- Consider compliance and security requirements when designing OU structure
47+
48+
### Additional Resources
49+
50+
- [Understanding SCP Effects on Permissions](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scp.html)
51+
- [Multi-account Strategy Design Principles](https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/organizations.html)
52+
- [AWS Organizations and SCP Evolution Case Studies](https://aws.amazon.com/organizations/getting-started/)
53+
54+
This implementation guide assumes familiarity with AWS Organizations and SCP concepts. For additional guidance, please consult the AWS documentation.
55+
56+
### Prerequisites
57+
AWS Organizations enabled
58+
Administrator access to AWS Management Account
59+
AWS CDK v2.x installed
60+
Node.js 18.x or later
61+
AWS CLI v2 configured
62+
### Installation
63+
Clone the repository:
64+
git clone https://github.com/aws-samples/aws-enterprise-tagging-governance
65+
cd Tagging-Governance
66+
Install dependencies:
67+
npm install
68+
Deploy the stack:
69+
cdk deploy
70+
Testing
71+
The project includes comprehensive test scenarios for validating tag enforcement:
72+
73+
### Compliant resource creation
74+
Non-compliant scenarios
75+
Special pattern testing
76+
Comprehensive boundary testing
77+
Execute tests using the provided Lambda function:
78+
79+
aws lambda invoke --function-name TaggingTestFunction --payload file://tests/compliant-scenario.json response.json
80+
Test Scenarios
81+
| Scenario Type | Test Case | Expected Result |
82+
|--------------|-----------|-----------------|
83+
| Compliant | Full Compliance | Resource creation successful |
84+
| Non-Compliant | Missing Tags | Resource creation denied |
85+
| Non-Compliant | Invalid Values | Resource creation denied |
86+
| Special Pattern | Invalid Prefix | Resource creation denied |
87+
| Comprehensive | Multiple Violations | Resource creation denied |
88+
89+
## License
90+
See LICENSE for more information.
91+
92+
## Contributing
93+
See CONTRIBUTING for more information.
94+
95+
## Support
96+
Please submit bug reports and feature requests through our GitHub issues page.

Tagging-Governance/cdk.json

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"app": "npx ts-node --prefer-ts-exts bin/security_blog_sample_code.ts",
3+
"watch": {
4+
"include": [
5+
"**"
6+
],
7+
"exclude": [
8+
"README.md",
9+
"cdk*.json",
10+
"**/*.d.ts",
11+
"**/*.js",
12+
"tsconfig.json",
13+
"package*.json",
14+
"yarn.lock",
15+
"node_modules",
16+
"test"
17+
]
18+
},
19+
"context": {
20+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21+
"@aws-cdk/core:checkSecretUsage": true,
22+
"@aws-cdk/core:target-partitions": [
23+
"aws",
24+
"aws-cn"
25+
],
26+
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27+
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28+
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
29+
"@aws-cdk/aws-iam:minimizePolicies": true,
30+
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
31+
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
32+
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
33+
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
34+
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
35+
"@aws-cdk/core:enablePartitionLiterals": true,
36+
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
37+
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
38+
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
39+
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
40+
"@aws-cdk/aws-route53-patters:useCertificate": true,
41+
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
42+
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
43+
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
44+
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
45+
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
46+
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
47+
"@aws-cdk/aws-redshift:columnId": true,
48+
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
49+
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
50+
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
51+
"@aws-cdk/aws-kms:aliasNameRef": true,
52+
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
53+
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
54+
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
55+
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
56+
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
57+
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
58+
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
59+
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
60+
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
61+
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
62+
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
63+
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
64+
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
65+
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
66+
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
67+
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
68+
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
69+
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
70+
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
71+
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
72+
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
73+
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
74+
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
75+
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
76+
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
77+
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
78+
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
79+
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
80+
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true
81+
}
82+
}

Tagging-Governance/jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
roots: ['<rootDir>/test'],
4+
testMatch: ['**/*.test.ts'],
5+
transform: {
6+
'^.+\\.tsx?$': 'ts-jest'
7+
}
8+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import * as cdk from 'aws-cdk-lib';
2+
import { Construct } from 'constructs';
3+
export declare class SecurityBlogSampleCodeStack extends cdk.Stack {
4+
constructor(scope: Construct, id: string, props?: cdk.StackProps);
5+
}

0 commit comments

Comments
 (0)