Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions Tagging-Governance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Tagging Governance using AWS Organizations
A solution for implementing enterprise-grade resource tagging governance using AWS Organizations Service Control Policies (SCPs).

## Overview
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.

## Features
Enforce required tags at resource creation time
Scale across multiple AWS accounts
Support for complex tag validation patterns
Integration with AWS Backup based on compliance tags
Support for hybrid and edge deployment patterns
Architecture
The solution implements tagging governance through:

## Service Control Policies (SCPs) for tag enforcement
AWS Organizations for policy management
AWS Lambda for testing and validation
AWS Backup for automated backup policies

## Important Notes on SCP Implementation

### ⚠️ Testing and Deployment Strategy

The service control policies in this repository are provided as examples only. Before implementing any SCPs:

1. **Thoroughly test** all policies before attachment
2. Test in an isolated environment that mirrors your production setup
3. Use a gradual deployment approach:
- Start with specific, limited-scope OUs
- Gradually expand to broader OUs
- Monitor and verify impact at each stage

### Understanding SCP Behavior

- SCPs provide coarse-grained guardrails but **do not grant permissions**
- Administrators must still manage identity-based and resource-based policies
- Effective permissions are the **logical intersection** of:
- Service Control Policy/Resource Control Policy
- Identity Policy or Resource Policy

### Organizational Design Recommendations

- Organize accounts by function, compliance requirements, or control sets
- Avoid mirroring organizational reporting structure
- Consider compliance and security requirements when designing OU structure

### Additional Resources

- [Understanding SCP Effects on Permissions](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scp.html)
- [Multi-account Strategy Design Principles](https://docs.aws.amazon.com/prescriptive-guidance/latest/security-reference-architecture/organizations.html)
- [AWS Organizations and SCP Evolution Case Studies](https://aws.amazon.com/organizations/getting-started/)

This implementation guide assumes familiarity with AWS Organizations and SCP concepts. For additional guidance, please consult the AWS documentation.

### Prerequisites
AWS Organizations enabled
Administrator access to AWS Management Account
AWS CDK v2.x installed
Node.js 18.x or later
AWS CLI v2 configured
### Installation
Clone the repository:
git clone https://github.com/aws-samples/aws-enterprise-tagging-governance
cd Tagging-Governance
Install dependencies:
npm install
Deploy the stack:
cdk deploy
Testing
The project includes comprehensive test scenarios for validating tag enforcement:

### Compliant resource creation
Non-compliant scenarios
Special pattern testing
Comprehensive boundary testing
Execute tests using the provided Lambda function:

aws lambda invoke --function-name TaggingTestFunction --payload file://tests/compliant-scenario.json response.json
Test Scenarios
| Scenario Type | Test Case | Expected Result |
|--------------|-----------|-----------------|
| Compliant | Full Compliance | Resource creation successful |
| Non-Compliant | Missing Tags | Resource creation denied |
| Non-Compliant | Invalid Values | Resource creation denied |
| Special Pattern | Invalid Prefix | Resource creation denied |
| Comprehensive | Multiple Violations | Resource creation denied |

## License
See LICENSE for more information.

## Contributing
See CONTRIBUTING for more information.

## Support
Please submit bug reports and feature requests through our GitHub issues page.
82 changes: 82 additions & 0 deletions Tagging-Governance/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"app": "npx ts-node --prefer-ts-exts bin/security_blog_sample_code.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true
}
}
8 changes: 8 additions & 0 deletions Tagging-Governance/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/test'],
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
}
};
5 changes: 5 additions & 0 deletions Tagging-Governance/lib/security_blog_sample_code-stack.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
export declare class SecurityBlogSampleCodeStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps);
}
Loading