Reputation: 14736
I have a requirement to deploy config rules conditionally based on certain parameters. Here below is one
config.ManagedRule(self, "AccessKeysRotated",
identifier=config.ManagedRuleIdentifiers.ACCESS_KEYS_ROTATED,
input_parameters={
"max_access_key_age": 60
},
maximum_execution_frequency=config.MaximumExecutionFrequency.TWELVE_HOURS
)
Here below is another one
config.ManagedRule(self, "S3BucketLogging",
identifier=config.ManagedRuleIdentifiers.S3_BUCKET_LOGGING_ENABLED,
config_rule_name="S3BucketLogging"
)
The managed rule identifiers are in the hundreds. I don't want to have all of these in one big file but each rule stored in a separate file. I can then read off a dynamodb where I store the account name and a csv list of rules that pertain to that account. Each item in the csv can be a single file which has a single rule. Is there a way to do this?
Upvotes: 0
Views: 367
Reputation: 25649
Sure. Create a aws_config_stack
, which you deploy once per account/region pair.
In the constructor, get the environment's rule names from DynamoDB with a get_item
SDK call. The boto3
command gets called at synth-time, which is keeping with CDK best practices.
Define the rules in whatever files you want. Wrap each rule in a function that accepts a scope and returns the rule:
# make_access_keys_rotated_rule.py
# One function per ManagedRule. Add to the rule dictionary. Called by `make_rule`
def make_access_keys_rotated_rule(scope: Construct) -> config.ManagedRule:
return config.ManagedRule(scope, "AccessKeysRotated",
identifier=config.ManagedRuleIdentifiers.ACCESS_KEYS_ROTATED,
input_parameters={
"max_access_key_age": 60
},
maximum_execution_frequency=config.MaximumExecutionFrequency.TWELVE_HOURS
)
Add each rule function to a dictionary, where the keys are the rule name. Perhaps add the dictionary lookup logic to a method in your aws_config_stack
subclass. A make_rule
method looks up the rule-making function by name and executes it, adding a single rule to the stack.
# aws_config_stack.py method
# Look up and execute a ManagedRule function by rule name. Called in the constructor.
def make_rule(self: Construct, rule_name: str) -> config.ManagedRule:
rule_dict = {
"AccessKeysRotated": make_access_keys_rotated_rule
}
return rule_dict[rule_name](self)
Finally, in the stack constructor, call make_rule
for every name in the rule-name list from DynamoDB.
# aws_config_stack.py constructor
rules: list[config.ManagedRule] = [self.make_rule(r) for r in rule_names_from_dynamo]
After synth, a cdk diff
should reveal rules being added and deleted from the stack to match the list from DynamoDB.
P.S. Optionally add the Delivery Channel (CfnDeliveryChannel
+ Bucket
) resources and Configuration Recorder (CfnConfigurationRecorder
+ Role
) resources to the same stack to have the CDK fully manage the AWS Config resources.
Upvotes: 1
Reputation: 2400
You can use a config file strategy instead if you would prefer. Similar to the answer by @fedonev, you can create a json file that holds the information for a particular set of rules. Then you can add conditionals before and a loop around loading the json file. Then you can use a trick of python and the fact that every one of these properties is a kwarg, and kwargs are dictionary.
Something like:
json_file:
{
"AccessKeysRotated": {
"identifier": "ACCESS_KEYS_ROTATED",
"input_parameters": {
"max_access_key_age": 60
},
"maximum_execution_frequency": "TWELVE_HOURS"
}
}
and then you load this into a python dict using json.loads.
you'll need to update the Enum values to the same classes, luckily the names can be used for that:
if "identifier" in your_config.keys():
your_config["identifier"] = config.ManagedRuleIdentifiers(your_config["identifier"])
(that can be done a lot more elegantly, but you mentioned in a comment your new to python so that is fine for now)
and finally, the trick: Because every property on a CDK object is a kwarg (keyword argument) and kwargs are stored as a dictionary behind the scenes, and have default values (thats what defines them as kwargs) you can - even with different keys - use:
## load json files that you need into a dict of dict objects
## make sure the enum values are updated from strings to enums
for key, config_setting in my_configs.items():
config.ManagedRule(self, key, **config_setting)
the key being the logical id of the object (so the "AccessKeysRotated" in your json file) and the ** syntax merging the dictionary of kwargs with the defaults. Because it merges your input second (its basically kwargs = {**kwargs, **config_setting}
your values override any defaults in the kwarg dict and boom. done.
Some tweaking will be needed undoubtedly, but this is the basic idea.
Upvotes: 0