Skip to content

Commit

Permalink
aws#21441 add Custom Policy Rule Constructs
Browse files Browse the repository at this point in the history
  • Loading branch information
watany-dev committed Aug 28, 2022
1 parent 62cbcde commit 684e23c
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 2 deletions.
56 changes: 54 additions & 2 deletions packages/@aws-cdk/aws-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,60 @@ new config.CloudFormationStackNotificationCheck(this, 'NotificationCheck', {
### Custom rules

You can develop custom rules and add them to AWS Config. You associate each custom rule with an
AWS Lambda function, which contains the logic that evaluates whether your AWS resources comply
with the rule.
AWS Lambda function and Guard.

#### Custom Lambda Rules

Lambda function which contains the logic that evaluates whether your AWS resources comply with the rule.

```ts
// Lambda function containing logic that evaluates compliance with the rule.
const evalComplianceFn = new lambda.Function(this, "CustomFunction", {
code: lambda.AssetCode.fromInline(
"exports.handler = (event) => console.log(event);"
),
handler: "index.handler",
runtime: lambda.Runtime.NODEJS_14_X,
});

// A custom rule that runs on configuration changes of EC2 instances
const customRule = new config.CustomRule(this, "Custom", {
configurationChanges: true,
lambdaFunction: evalComplianceFn,
ruleScope: config.RuleScope.fromResource(config.ResourceType.EC2_INSTANCE),
});
```

#### Custom Policy Rules

Guard which contains the logic that evaluates whether your AWS resources comply with the rule.

```ts
const samplePolicyText = `
# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables
let status = ['ACTIVE']
rule tableisactive when
resourceType == "AWS::DynamoDB::Table" {
configuration.tableStatus == %status
}
rule checkcompliance when
resourceType == "AWS::DynamoDB::Table"
tableisactive {
let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus
%pitr == "ENABLED"
}
`;

new config.CustomPolicy(stack, "Custom", {
policyText: samplePolicyText,
enableDebugLog: true,
ruleScope: config.RuleScope.fromResources([
config.ResourceType.DYNAMODB_TABLE,
]),
});
```

### Triggers

Expand Down
81 changes: 81 additions & 0 deletions packages/@aws-cdk/aws-config/lib/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,87 @@ export class CustomRule extends RuleNew {
}
}

/**
* Construction properties for a CustomPolicy.
*/
export interface CustomPolicyProps extends RuleProps {
/**
* The policy definition containing the logic for your AWS Config Custom Policy rule.
*/
readonly policyText: string;

/**
* The boolean expression for enabling debug logging for your AWS Config Custom Policy rule.
*
* @default false
*/
readonly enableDebugLog?: boolean;
}
/**
* A new custom policy.
*
* @resource AWS::Config::ConfigRule
*/
export class CustomPolicy extends RuleNew {
/** @attribute */
public readonly configRuleName: string;

/** @attribute */
public readonly configRuleArn: string;

/** @attribute */
public readonly configRuleId: string;

/** @attribute */
public readonly configRuleComplianceType: string;

constructor(scope: Construct, id: string, props: CustomPolicyProps) {
super(scope, id, {
physicalName: props.configRuleName,
});

if (!props.policyText || [...props.policyText].length === 0) {
throw new Error('Policy Text cannot be empty.');
}
if ( [...props.policyText].length > 10000 ) {
throw new Error('Policy Text is limited to 10,000 characters or less.');
}

const sourceDetails: any[] = [];
this.ruleScope = props.ruleScope;

sourceDetails.push({
eventSource: 'aws.config',
messageType: 'ConfigurationItemChangeNotification',
});
sourceDetails.push({
eventSource: 'aws.config',
messageType: 'OversizedConfigurationItemChangeNotification',
});
const rule = new CfnConfigRule(this, 'Resource', {
configRuleName: this.physicalName,
description: props.description,
inputParameters: props.inputParameters,
scope: Lazy.any({ produce: () => renderScope(this.ruleScope) }), // scope can use values such as stack id (see CloudFormationStackDriftDetectionCheck)
source: {
owner: 'CUSTOM_POLICY',
sourceDetails,
customPolicyDetails: {
enableDebugLogDelivery: props.enableDebugLog,
policyRuntime: 'guard-2.x.x',
policyText: props.policyText,
},
},
});

this.configRuleName = rule.ref;
this.configRuleArn = rule.attrArn;
this.configRuleId = rule.attrConfigRuleId;
this.configRuleComplianceType = rule.attrComplianceType;
this.isCustomWithChanges = true;
}
}

/**
* Managed rules that are supported by AWS Config.
* @see https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"@aws-cdk/aws-events-targets": "0.0.0",
"@aws-cdk/cdk-build-tools": "0.0.0",
"@aws-cdk/integ-runner": "0.0.0",
"@aws-cdk/integ-tests": "0.0.0",
"@aws-cdk/cfn2ts": "0.0.0",
"@aws-cdk/pkglint": "0.0.0",
"@types/jest": "^27.5.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"Resources": {
"Custom8166710A": {
"Type": "AWS::Config::ConfigRule",
"Properties": {
"Source": {
"CustomPolicyDetails": {
"EnableDebugLogDelivery": true,
"PolicyRuntime": "guard-2.x.x",
"PolicyText": "\n# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables\nlet status = ['ACTIVE']\n\nrule tableisactive when\n resourceType == \"AWS::DynamoDB::Table\" {\n configuration.tableStatus == %status\n}\n\nrule checkcompliance when\n resourceType == \"AWS::DynamoDB::Table\"\n tableisactive {\n let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus\n %pitr == \"ENABLED\"\n}\n"
},
"Owner": "CUSTOM_POLICY",
"SourceDetails": [
{
"EventSource": "aws.config",
"MessageType": "ConfigurationItemChangeNotification"
},
{
"EventSource": "aws.config",
"MessageType": "OversizedConfigurationItemChangeNotification"
}
]
},
"Scope": {
"ComplianceResourceTypes": [
"AWS::EC2::Instance"
]
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"20.0.0"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "20.0.0",
"testCases": {
"aws-cdk-config-custompolicy-integ/DefaultTest": {
"stacks": [
"aws-cdk-config-custompolicy"
],
"assertionStack": "aws-cdk-config-custompolicy-integ/DefaultTest/DeployAssert"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
"properties": {
"file": "tree.json"
}
},
"aws-cdk-config-custompolicy": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "aws-cdk-config-custompolicy.template.json",
"validateOnSynth": false
},
"metadata": {
"/aws-cdk-config-custompolicy/Custom/Resource": [
{
"type": "aws:cdk:logicalId",
"data": "Custom8166710A"
}
]
},
"displayName": "aws-cdk-config-custompolicy"
},
"awscdkconfigcustompolicyintegDefaultTestDeployAssert4EE21D3A": {
"type": "aws:cloudformation:stack",
"environment": "aws://unknown-account/unknown-region",
"properties": {
"templateFile": "awscdkconfigcustompolicyintegDefaultTestDeployAssert4EE21D3A.template.json",
"validateOnSynth": false
},
"displayName": "aws-cdk-config-custompolicy-integ/DefaultTest/DeployAssert"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{
"version": "tree-0.1",
"tree": {
"id": "App",
"path": "",
"children": {
"Tree": {
"id": "Tree",
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.78"
}
},
"aws-cdk-config-custompolicy": {
"id": "aws-cdk-config-custompolicy",
"path": "aws-cdk-config-custompolicy",
"children": {
"Custom": {
"id": "Custom",
"path": "aws-cdk-config-custompolicy/Custom",
"children": {
"Resource": {
"id": "Resource",
"path": "aws-cdk-config-custompolicy/Custom/Resource",
"attributes": {
"aws:cdk:cloudformation:type": "AWS::Config::ConfigRule",
"aws:cdk:cloudformation:props": {
"source": {
"owner": "CUSTOM_POLICY",
"sourceDetails": [
{
"eventSource": "aws.config",
"messageType": "ConfigurationItemChangeNotification"
},
{
"eventSource": "aws.config",
"messageType": "OversizedConfigurationItemChangeNotification"
}
],
"customPolicyDetails": {
"enableDebugLogDelivery": true,
"policyRuntime": "guard-2.x.x",
"policyText": "\n# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables\nlet status = ['ACTIVE']\n\nrule tableisactive when\n resourceType == \"AWS::DynamoDB::Table\" {\n configuration.tableStatus == %status\n}\n\nrule checkcompliance when\n resourceType == \"AWS::DynamoDB::Table\"\n tableisactive {\n let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus\n %pitr == \"ENABLED\"\n}\n"
}
},
"scope": {
"complianceResourceTypes": [
"AWS::EC2::Instance"
]
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-config.CfnConfigRule",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-config.CustomPolicy",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
},
"aws-cdk-config-custompolicy-integ": {
"id": "aws-cdk-config-custompolicy-integ",
"path": "aws-cdk-config-custompolicy-integ",
"children": {
"DefaultTest": {
"id": "DefaultTest",
"path": "aws-cdk-config-custompolicy-integ/DefaultTest",
"children": {
"Default": {
"id": "Default",
"path": "aws-cdk-config-custompolicy-integ/DefaultTest/Default",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.1.78"
}
},
"DeployAssert": {
"id": "DeployAssert",
"path": "aws-cdk-config-custompolicy-integ/DefaultTest/DeployAssert",
"constructInfo": {
"fqn": "@aws-cdk/core.Stack",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/integ-tests.IntegTestCase",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/integ-tests.IntegTest",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/core.App",
"version": "0.0.0"
}
}
}
Loading

0 comments on commit 684e23c

Please sign in to comment.