AWS config documentation change
Summary
Added detailed documentation and code examples for AWS Config Custom Lambda rules, including configuration change-triggered evaluations, periodic evaluations, event structure analysis, and AWS RDK workflow guidance
Security assessment
The changes add documentation about implementing security/compliance checks through AWS Config rules using Lambda functions. While this relates to security posture management, there's no evidence of addressing a specific vulnerability. The examples demonstrate security best practices for compliance monitoring but don't disclose or fix existing vulnerabilities.
Diff
diff --git a/config/latest/developerguide/evaluate-config_develop-rules_lambda-functions.md b/config/latest/developerguide/evaluate-config_develop-rules_lambda-functions.md index 1be4ef673..a1fadf91f 100644 --- a//config/latest/developerguide/evaluate-config_develop-rules_lambda-functions.md +++ b//config/latest/developerguide/evaluate-config_develop-rules_lambda-functions.md @@ -10,0 +11,2 @@ You associate each custom rule with an Lambda function, which contains the logic +The AWS Rule Development Kit (RDK) is designed to support a "Compliance-as-Code" workflow that is intuitive and productive. It abstracts away much of the undifferentiated heavy lifting associated with deploying AWS Config rules backed by custom Lambda functions, and provides a streamlined develop-deploy-monitor iterative process. + @@ -12,0 +15,754 @@ For step-by-step instruction, see the [AWS Rule Development Kit (RDK) Documentat +AWS Lambda executes functions in response to events that are published by AWS services. The function for an AWS Config Custom Lambda rule receives an event that is published by AWS Config, and the function then uses data that it receives from the event and that it retrieves from the AWS Config API to evaluate the compliance of the rule. The operations in a function for a Config rule differ depending on whether it performs an evaluation that is triggered by configuration changes or triggered periodically. + +For information about common patterns within AWS Lambda functions, see [Programming Model](https://docs.aws.amazon.com/lambda/latest/dg/programming-model-v2.html) in the _AWS Lambda Developer Guide_. + +Example Function for Evaluations Triggered by Configuration Changes + + +AWS Config will invoke a function like the following example when it detects a configuration change for a resource that is within a custom rule's scope. + +If you use the AWS Config console to create a rule that is associated with a function like this example, choose **Configuration changes** as the trigger type. If you use the AWS Config API or AWS CLI to create the rule, set the `MessageType` attribute to `ConfigurationItemChangeNotification` and `OversizedConfigurationItemChangeNotification`. These settings enable your rule to be triggered whenever AWS Config generates a configuration item or an oversized configuration item as a result of a resource change. + +This example evaluates your resources and checks whether the instances match the resource type, `AWS::EC2::Instance`. The rule is triggered when AWS Config generates a configuration item or an oversized configuration item notification. + + + 'use strict'; + + import { ConfigServiceClient, GetResourceConfigHistoryCommand, PutEvaluationsCommand } from "@aws-sdk/client-config-service"; + + const configClient = new ConfigServiceClient({}); + + // Helper function used to validate input + function checkDefined(reference, referenceName) { + if (!reference) { + throw new Error(`Error: ${referenceName} is not defined`); + } + return reference; + } + + // Check whether the message type is OversizedConfigurationItemChangeNotification, + function isOverSizedChangeNotification(messageType) { + checkDefined(messageType, 'messageType'); + return messageType === 'OversizedConfigurationItemChangeNotification'; + } + + // Get the configurationItem for the resource using the getResourceConfigHistory API. + async function getConfiguration(resourceType, resourceId, configurationCaptureTime, callback) { + const input = { resourceType, resourceId, laterTime: new Date(configurationCaptureTime), limit: 1 }; + const command = new GetResourceConfigHistoryCommand(input); + await configClient.send(command).then( + (data) => { + callback(null, data.configurationItems[0]); + }, + (error) => { + callback(error, null); + } + ); + + } + + // Convert the oversized configuration item from the API model to the original invocation model. + function convertApiConfiguration(apiConfiguration) { + apiConfiguration.awsAccountId = apiConfiguration.accountId; + apiConfiguration.ARN = apiConfiguration.arn; + apiConfiguration.configurationStateMd5Hash = apiConfiguration.configurationItemMD5Hash; + apiConfiguration.configurationItemVersion = apiConfiguration.version; + apiConfiguration.configuration = JSON.parse(apiConfiguration.configuration); + if ({}.hasOwnProperty.call(apiConfiguration, 'relationships')) { + for (let i = 0; i < apiConfiguration.relationships.length; i++) { + apiConfiguration.relationships[i].name = apiConfiguration.relationships[i].relationshipName; + } + } + return apiConfiguration; + } + + // Based on the message type, get the configuration item either from the configurationItem object in the invoking event or with the getResourceConfigHistory API in the getConfiguration function. + async function getConfigurationItem(invokingEvent, callback) { + checkDefined(invokingEvent, 'invokingEvent'); + if (isOverSizedChangeNotification(invokingEvent.messageType)) { + const configurationItemSummary = checkDefined(invokingEvent.configurationItemSummary, 'configurationItemSummary'); + await getConfiguration(configurationItemSummary.resourceType, configurationItemSummary.resourceId, configurationItemSummary.configurationItemCaptureTime, (err, apiConfigurationItem) => { + if (err) { + callback(err); + } + const configurationItem = convertApiConfiguration(apiConfigurationItem); + callback(null, configurationItem); + }); + } else { + checkDefined(invokingEvent.configurationItem, 'configurationItem'); + callback(null, invokingEvent.configurationItem); + } + } + + // Check whether the resource has been deleted. If the resource was deleted, then the evaluation returns not applicable. + function isApplicable(configurationItem, event) { + checkDefined(configurationItem, 'configurationItem'); + checkDefined(event, 'event'); + const status = configurationItem.configurationItemStatus; + const eventLeftScope = event.eventLeftScope; + return (status === 'OK' || status === 'ResourceDiscovered') && eventLeftScope === false; + } + + // In this example, the resource is compliant if it is an instance and its type matches the type specified as the desired type. + // If the resource is not an instance, then this resource is not applicable. + function evaluateChangeNotificationCompliance(configurationItem, ruleParameters) { + checkDefined(configurationItem, 'configurationItem'); + checkDefined(configurationItem.configuration, 'configurationItem.configuration'); + checkDefined(ruleParameters, 'ruleParameters'); + + if (configurationItem.resourceType !== 'AWS::EC2::Instance') { + return 'NOT_APPLICABLE'; + } else if (ruleParameters.desiredInstanceType === configurationItem.configuration.instanceType) { + return 'COMPLIANT'; + } + return 'NON_COMPLIANT'; + } + + // Receives the event and context from AWS Lambda. + export const handler = async (event, context) => { + checkDefined(event, 'event'); + const invokingEvent = JSON.parse(event.invokingEvent); + const ruleParameters = JSON.parse(event.ruleParameters); + await getConfigurationItem(invokingEvent, async (err, configurationItem) => { + + let compliance = 'NOT_APPLICABLE'; + let annotation = ''; + const putEvaluationsRequest = {}; + if (isApplicable(configurationItem, event)) { + // Invoke the compliance checking function. + compliance = evaluateChangeNotificationCompliance(configurationItem, ruleParameters); + if (compliance === "NON_COMPLIANT") { + annotation = "This is an annotation describing why the resource is not compliant."; + } + } + // Initializes the request that contains the evaluation results. + if (annotation) { + putEvaluationsRequest.Evaluations = [ + { + ComplianceResourceType: configurationItem.resourceType, + ComplianceResourceId: configurationItem.resourceId, + ComplianceType: compliance, + OrderingTimestamp: new Date(configurationItem.configurationItemCaptureTime), + Annotation: annotation + }, + ]; + } else { + putEvaluationsRequest.Evaluations = [ + { + ComplianceResourceType: configurationItem.resourceType, + ComplianceResourceId: configurationItem.resourceId, + ComplianceType: compliance, + OrderingTimestamp: new Date(configurationItem.configurationItemCaptureTime), + }, + ]; + } + putEvaluationsRequest.ResultToken = event.resultToken; + + // Sends the evaluation results to AWS Config. + await configClient.send(new PutEvaluationsCommand(putEvaluationsRequest)); + }); + }; + +###### Function Operations + +The function performs the following operations at runtime: + + 1. The function runs when AWS Lambda passes the `event` object to the `handler` function. In this example, the function accepts the optional `callback` parameter, which it uses to return information to the caller. AWS Lambda also passes a `context` object, which contains information and methods that the function can use while it runs. Note that in newer versions of Lambda, context is no longer used. + + 2. The function checks whether the `messageType` for the event is a configuration item or an oversized configuration item, and then returns the configuration item. + + 3. The handler calls the `isApplicable` function to determine whether the resource was deleted. + +###### Note + +Rules reporting on deleted resources should return the evaluation result of `NOT_APPLICABLE` in order to avoid unnecessary rule evaluations. + + 4. The handler calls the `evaluateChangeNotificationCompliance` function and passes the `configurationItem` and `ruleParameters` objects that AWS Config published in the event. + +The function first evaluates whether the resource is an EC2 instance. If the resource is not an EC2 instance, the function returns a compliance value of `NOT_APPLICABLE`. + +The function then evaluates whether the `instanceType` attribute in the configuration item is equal to the `desiredInstanceType` parameter value. If the values are equal, the function returns `COMPLIANT`. If the values are not equal, the function returns `NON_COMPLIANT`. + + 5. The handler prepares to send the evaluation results to AWS Config by initializing the `putEvaluationsRequest` object. This object includes the `Evaluations` parameter, which identifies the compliance result, the resource type, and the ID of the resource that was evaluated. The `putEvaluationsRequest` object also includes the result token from the event, which identifies the rule and the event for AWS Config. + + 6. The handler sends the evaluation results to AWS Config by passing the object to the `putEvaluations` method of the `config` client. + + + + +Example Function for Periodic Evaluations + + +AWS Config will invoke a function like the following example for periodic evaluations. Periodic evaluations occur at the frequency that you specify when you define the rule in AWS Config. + +If you use the AWS Config console to create a rule that is associated with a function like this example, choose **Periodic** as the trigger type. If you use the AWS Config API or AWS CLI to create the rule, set the `MessageType` attribute to `ScheduledNotification`. + +This example checks whether the total number of a specified resource exceeds a specified maximum. + + + 'use strict'; + import { ConfigServiceClient, ListDiscoveredResourcesCommand, PutEvaluationsCommand } from "@aws-sdk/client-config-service"; + + const configClient = new ConfigServiceClient({});