创建 Amazon Config 自定义 Lambda 规则 - Amazon Config
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

创建 Amazon Config 自定义 Lambda 规则

您可以开发自定义规则,并 Amazon Config 使用 Amazon Lambda 函数将其添加到其中。

您可以将每条自定义规则与一个 Lambda 函数相关联,该函数包含评估您的 Amazon 资源是否符合该规则的逻辑。将此函数与您的规则关联后,该规则会定期或因响应配置更改而调用该函数。然后,该函数会评估您的资源是否符合您的规则,并将其评估结果发送到 Amazon Config。

Amazon 规则开发套件 (RDK) 旨在支持直观且高效的 “合规即代码” 工作流程。它抽象了与部署由自定义 Lambda 函数支持的 Amazon Config 规则相关的大部分无差别的繁重工作,并提供了简化的迭代流程。 develop-deploy-monitor

有关 step-by-step说明,请参阅Amazon 规则开发套件 (RDK) 文档

Amazon Lambda 执行函数以响应 Amazon 服务发布的事件。 Amazon Config 自定义 Lambda 规则的函数接收由发布的事件 Amazon Config,然后该函数使用从该事件中接收的数据以及从 Amazon Config API 检索的数据来评估规则的合规性。用于 Config 规则的函数的运作方式会因其执行的评估是由配置更改触发还是定期触发而有所不同。

有关 Amazon Lambda 函数中常见模式的信息,请参阅《Amazon Lambda 开发人员指南》中的编程模型

Example Function for Evaluations Triggered by Configuration Changes

Amazon Config 当它检测到自定义规则范围内的资源的配置更改时,将调用如下例所示的函数。

如果您使用 Amazon Config 控制台创建与函数相关联的规则(例如此示例),请选择配置更改作为触发器类型。如果您使用 Amazon Config API 或创建规则, Amazon CLI 请将MessageType属性设置为ConfigurationItemChangeNotificationOversizedConfigurationItemChangeNotification。通过这些设置,无论何时由于资源更改而 Amazon Config 生成配置项目或超大配置项目,都可触发您的规则。

此示例评估您的资源并检查实例是否匹配资源类型 AWS::EC2::Instance。此规则在 Amazon Config 生成配置项或过大配置项通知时触发。

'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)); }); };
函数运作

本函数在运行时执行以下操作:

  1. 当将event对象 Amazon Lambda 传递给函数时,该handler函数就会运行。在此示例中,该函数接受可选callback参数,该参数用于向调用者返回信息。 Amazon Lambda 还会传递一个context对象,其中包含函数在运行时可以使用的信息和方法。请注意,在新版本的 Lambda 中,不再使用上下文。

  2. 此函数检查事件的 messageType 是配置项还是过大配置项,然后返回配置项。

  3. 处理程序调用 isApplicable 函数来确定资源是否已删除。

    注意

    报告已删除资源的规则应返回 NOT_APPLICABLE 的评估结果,以避免不必要的规则评估。

  4. 处理程序调用该evaluateChangeNotificationCompliance函数并传递在事件中 Amazon Config 发布的configurationItemruleParameters对象。

    该函数首先评估资源是否为 EC2 实例。如果资源不是 EC2 实例,则该函数返回的合规性值为NOT_APPLICABLE

    然后,函数评估配置项中的 instanceType 属性是否与 desiredInstanceType 参数值一致。如果值相等,该函数将返回 COMPLIANT。如果值不相等,该函数将返回 NON_COMPLIANT

  5. 处理程序准备 Amazon Config 通过初始化putEvaluationsRequest对象将评估结果发送到。该对象包含 Evaluations 参数,这一参数用于识别受评估资源的合规性结果、资源类型和 ID。该putEvaluationsRequest对象还包括事件的结果标记,用于标识规则和事件 Amazon Config。

  6. Amazon Config 通过将对象传递给config客户端putEvaluations的方法,处理程序将评估结果发送到。

Example Function for Periodic Evaluations

Amazon Config 将调用如下例所示的函数进行定期评估。定期评估按您在 Amazon Config中定义规则时指定的频率进行。

如果您使用 Amazon Config 控制台创建与函数相关联的规则(例如此示例),请选择周期作为触发器类型。如果您使用 Amazon Config API 或创建规则, Amazon CLI 请将MessageType属性设置为ScheduledNotification

本示例会检查指定资源的总数是否超出指定的最大值。

'use strict'; import { ConfigServiceClient, ListDiscoveredResourcesCommand, PutEvaluationsCommand } from "@aws-sdk/client-config-service"; const configClient = new ConfigServiceClient({}); // Receives the event and context from AWS Lambda. export const handler = async (event, context, callback) => { // Parses the invokingEvent and ruleParameters values, which contain JSON objects passed as strings. var invokingEvent = JSON.parse(event.invokingEvent), ruleParameters = JSON.parse(event.ruleParameters), numberOfResources = 0; if (isScheduledNotification(invokingEvent) && hasValidRuleParameters(ruleParameters, callback)) { await countResourceTypes(ruleParameters.applicableResourceType, "", numberOfResources, async function (err, count) { if (err === null) { var putEvaluationsRequest; const compliance = evaluateCompliance(ruleParameters.maxCount, count); var annotation = ''; if (compliance === "NON_COMPLIANT") { annotation = "Description of why the resource is not compliant."; } // Initializes the request that contains the evaluation results. if (annotation) { putEvaluationsRequest = { Evaluations: [{ // Applies the evaluation result to the AWS account published in the event. ComplianceResourceType: 'AWS::::Account', ComplianceResourceId: event.accountId, ComplianceType: compliance, OrderingTimestamp: new Date(), Annotation: annotation }], ResultToken: event.resultToken }; } else { putEvaluationsRequest = { Evaluations: [{ // Applies the evaluation result to the AWS account published in the event. ComplianceResourceType: 'AWS::::Account', ComplianceResourceId: event.accountId, ComplianceType: compliance, OrderingTimestamp: new Date() }], ResultToken: event.resultToken }; } // Sends the evaluation results to AWS Config. try { await configClient.send(new PutEvaluationsCommand(putEvaluationsRequest)); } catch (e) { callback(e, null); } } else { callback(err, null); } }); } else { console.log("Invoked for a notification other than Scheduled Notification... Ignoring."); } }; // Checks whether the invoking event is ScheduledNotification. function isScheduledNotification(invokingEvent) { return (invokingEvent.messageType === 'ScheduledNotification'); } // Checks the rule parameters to see if they are valid function hasValidRuleParameters(ruleParameters, callback) { // Regular express to verify that applicable resource given is a resource type const awsResourcePattern = /^AWS::(\w*)::(\w*)$/; const isApplicableResourceType = awsResourcePattern.test(ruleParameters.applicableResourceType); // Check to make sure the maxCount in the parameters is an integer const maxCountIsInt = !isNaN(ruleParameters.maxCount) && parseInt(Number(ruleParameters.maxCount)) == ruleParameters.maxCount && !isNaN(parseInt(ruleParameters.maxCount, 10)); if (!isApplicableResourceType) { callback("The applicableResourceType parameter is not a valid resource type.", null); } if (!maxCountIsInt) { callback("The maxCount parameter is not a valid integer.", null); } return isApplicableResourceType && maxCountIsInt; } // Checks whether the compliance conditions for the rule are violated. function evaluateCompliance(maxCount, actualCount) { if (actualCount > maxCount) { return "NON_COMPLIANT"; } else { return "COMPLIANT"; } } // Counts the applicable resources that belong to the AWS account. async function countResourceTypes(applicableResourceType, nextToken, count, callback) { const input = { resourceType: applicableResourceType, nextToken: nextToken }; const command = new ListDiscoveredResourcesCommand(input); try { const response = await configClient.send(command); count = count + response.resourceIdentifiers.length; if (response.nextToken !== undefined && response.nextToken != null) { countResourceTypes(applicableResourceType, response.nextToken, count, callback); } callback(null, count); } catch (e) { callback(e, null); } return count; }
函数运作

本函数在运行时执行以下操作:

  1. 当将event对象 Amazon Lambda 传递给函数时,该handler函数就会运行。在此示例中,该函数接受可选callback参数,该参数用于向调用者返回信息。 Amazon Lambda 还会传递一个context对象,其中包含函数在运行时可以使用的信息和方法。请注意,在新版本的 Lambda 中,不再使用上下文。

  2. 为计数指定类型的资源,处理程序会调用 countResourceTypes 函数,而且它传递其从事件收到的 applicableResourceType 参数。countResourceTypes 函数调用 listDiscoveredResources 客户端的 config 方法,该方法返回适用资源的标识符列表。该函数使用此列表的长度来确定适用资源的数量,而且它将此计数返回到处理程序。

  3. 处理程序准备 Amazon Config 通过初始化putEvaluationsRequest对象将评估结果发送到。此对象包括Evaluations参数,该参数标识合规性结果和 Amazon Web Services 账户 在事件中发布的结果。您可以使用 Evaluations 参数将结果应用于 Amazon Config支持的任何资源类型。该putEvaluationsRequest对象还包括事件的结果标记,用于标识规则和事件 Amazon Config。

  4. putEvaluationsRequest 对象中,处理程序调用 evaluateCompliance 函数。此函数测试适用资源的数量是否超出分配给事件所提供的 maxCount 参数的最大值。如果资源的数量超出最大值,函数将返回 NON_COMPLIANT。如果资源的数量没有超出最大值,函数将返回 COMPLIANT

  5. Amazon Config 通过将对象传递给config客户端putEvaluations的方法,处理程序将评估结果发送到。

Amazon Lambda 执行函数以响应 Amazon 服务发布的事件。 Amazon Config 自定义 Lambda 规则的函数接收由发布的事件 Amazon Config,然后该函数使用从该事件中接收的数据以及从 Amazon Config API 检索的数据来评估规则的合规性。用于 Config 规则的函数的运作方式会因其执行的评估是由配置更改触发还是定期触发而有所不同。

有关 Amazon Lambda 函数中常见模式的信息,请参阅《Amazon Lambda 开发人员指南》中的编程模型

Example Function for Evaluations Triggered by Configuration Changes

Amazon Config 当它检测到自定义规则范围内的资源的配置更改时,将调用如下例所示的函数。

如果您使用 Amazon Config 控制台创建与函数相关联的规则(例如此示例),请选择配置更改作为触发器类型。如果您使用 Amazon Config API 或创建规则, Amazon CLI 请将MessageType属性设置为ConfigurationItemChangeNotificationOversizedConfigurationItemChangeNotification。通过这些设置,无论何时由于资源更改而 Amazon Config 生成配置项目或超大配置项目,都可触发您的规则。

import botocore import boto3 import json import datetime # Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account). ASSUME_ROLE_MODE = False # This gets the client after assuming the Config service role # either in the same AWS account or cross-account. def get_client(service, event): """Return the service boto client. It should be used instead of directly calling the client. Keyword arguments: service -- the service name used for calling the boto.client() event -- the event variable given in the lambda handler """ if not ASSUME_ROLE_MODE: return boto3.client(service) credentials = get_assume_role_credentials(event["executionRoleArn"]) return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken'] ) # Helper function used to validate input def check_defined(reference, reference_name): if not reference: raise Exception('Error: ', reference_name, 'is not defined') return reference # Check whether the message is OversizedConfigurationItemChangeNotification or not def is_oversized_changed_notification(message_type): check_defined(message_type, 'messageType') return message_type == 'OversizedConfigurationItemChangeNotification' # Get configurationItem using getResourceConfigHistory API # in case of OversizedConfigurationItemChangeNotification def get_configuration(resource_type, resource_id, configuration_capture_time): result = AWS_CONFIG_CLIENT.get_resource_config_history( resourceType=resource_type, resourceId=resource_id, laterTime=configuration_capture_time, limit=1) configurationItem = result['configurationItems'][0] return convert_api_configuration(configurationItem) # Convert from the API model to the original invocation model def convert_api_configuration(configurationItem): for k, v in configurationItem.items(): if isinstance(v, datetime.datetime): configurationItem[k] = str(v) configurationItem['awsAccountId'] = configurationItem['accountId'] configurationItem['ARN'] = configurationItem['arn'] configurationItem['configurationStateMd5Hash'] = configurationItem['configurationItemMD5Hash'] configurationItem['configurationItemVersion'] = configurationItem['version'] configurationItem['configuration'] = json.loads(configurationItem['configuration']) if 'relationships' in configurationItem: for i in range(len(configurationItem['relationships'])): configurationItem['relationships'][i]['name'] = configurationItem['relationships'][i]['relationshipName'] return configurationItem # Based on the type of message get the configuration item # either from configurationItem in the invoking event # or using the getResourceConfigHistory API in getConfiguration function. def get_configuration_item(invokingEvent): check_defined(invokingEvent, 'invokingEvent') if is_oversized_changed_notification(invokingEvent['messageType']): configurationItemSummary = check_defined(invokingEvent['configurationItemSummary'], 'configurationItemSummary') return get_configuration(configurationItemSummary['resourceType'], configurationItemSummary['resourceId'], configurationItemSummary['configurationItemCaptureTime']) return check_defined(invokingEvent['configurationItem'], 'configurationItem') # Check whether the resource has been deleted. If it has, then the evaluation is unnecessary. def is_applicable(configurationItem, event): try: check_defined(configurationItem, 'configurationItem') check_defined(event, 'event') except: return True status = configurationItem['configurationItemStatus'] eventLeftScope = event['eventLeftScope'] if status == 'ResourceDeleted': print("Resource Deleted, setting Compliance Status to NOT_APPLICABLE.") return (status == 'OK' or status == 'ResourceDiscovered') and not eventLeftScope def get_assume_role_credentials(role_arn): sts_client = boto3.client('sts') try: assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution") return assume_role_response['Credentials'] except botocore.exceptions.ClientError as ex: # Scrub error message for any internal account info leaks if 'AccessDenied' in ex.response['Error']['Code']: ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role." else: ex.response['Error']['Message'] = "InternalError" ex.response['Error']['Code'] = "InternalError" raise ex def evaluate_change_notification_compliance(configuration_item, rule_parameters): check_defined(configuration_item, 'configuration_item') check_defined(configuration_item['configuration'], 'configuration_item[\'configuration\']') if rule_parameters: check_defined(rule_parameters, 'rule_parameters') if (configuration_item['resourceType'] != 'AWS::EC2::Instance'): return 'NOT_APPLICABLE' elif rule_parameters.get('desiredInstanceType'): if (configuration_item['configuration']['instanceType'] in rule_parameters['desiredInstanceType']): return 'COMPLIANT' return 'NON_COMPLIANT' def lambda_handler(event, context): global AWS_CONFIG_CLIENT check_defined(event, 'event') invoking_event = json.loads(event['invokingEvent']) rule_parameters = {} if 'ruleParameters' in event: rule_parameters = json.loads(event['ruleParameters']) compliance_value = 'NOT_APPLICABLE' AWS_CONFIG_CLIENT = get_client('config', event) configuration_item = get_configuration_item(invoking_event) if is_applicable(configuration_item, event): compliance_value = evaluate_change_notification_compliance( configuration_item, rule_parameters) response = AWS_CONFIG_CLIENT.put_evaluations( Evaluations=[ { 'ComplianceResourceType': invoking_event['configurationItem']['resourceType'], 'ComplianceResourceId': invoking_event['configurationItem']['resourceId'], 'ComplianceType': compliance_value, 'OrderingTimestamp': invoking_event['configurationItem']['configurationItemCaptureTime'] }, ], ResultToken=event['resultToken'])
函数运作

本函数在运行时执行以下操作:

  1. 当将event对象 Amazon Lambda 传递给函数时,该handler函数就会运行。在此示例中,该函数接受可选callback参数,该参数用于向调用者返回信息。 Amazon Lambda 还会传递一个context对象,其中包含函数在运行时可以使用的信息和方法。请注意,在新版本的 Lambda 中,不再使用上下文。

  2. 此函数检查事件的 messageType 是配置项还是过大配置项,然后返回配置项。

  3. 处理程序调用 isApplicable 函数来确定资源是否已删除。

    注意

    报告已删除资源的规则应返回 NOT_APPLICABLE 的评估结果,以避免不必要的规则评估。

  4. 处理程序调用该evaluateChangeNotificationCompliance函数并传递在事件中 Amazon Config 发布的configurationItemruleParameters对象。

    该函数首先评估资源是否为 EC2 实例。如果资源不是 EC2 实例,则该函数返回的合规性值为NOT_APPLICABLE

    然后,函数评估配置项中的 instanceType 属性是否与 desiredInstanceType 参数值一致。如果值相等,该函数将返回 COMPLIANT。如果值不相等,该函数将返回 NON_COMPLIANT

  5. 处理程序准备 Amazon Config 通过初始化putEvaluationsRequest对象将评估结果发送到。该对象包含 Evaluations 参数,这一参数用于识别受评估资源的合规性结果、资源类型和 ID。该putEvaluationsRequest对象还包括事件的结果标记,用于标识规则和事件 Amazon Config。

  6. Amazon Config 通过将对象传递给config客户端putEvaluations的方法,处理程序将评估结果发送到。

Example Function for Periodic Evaluations

Amazon Config 将调用如下例所示的函数进行定期评估。定期评估按您在 Amazon Config中定义规则时指定的频率进行。

如果您使用 Amazon Config 控制台创建与函数相关联的规则(例如此示例),请选择周期作为触发器类型。如果您使用 Amazon Config API 或创建规则, Amazon CLI 请将MessageType属性设置为ScheduledNotification

import botocore import boto3 import json import datetime # Set to True to get the lambda to assume the Role attached on the Config Service (useful for cross-account). ASSUME_ROLE_MODE = False DEFAULT_RESOURCE_TYPE = 'AWS::::Account' # This gets the client after assuming the Config service role # either in the same AWS account or cross-account. def get_client(service, event): """Return the service boto client. It should be used instead of directly calling the client. Keyword arguments: service -- the service name used for calling the boto.client() event -- the event variable given in the lambda handler """ if not ASSUME_ROLE_MODE: return boto3.client(service) credentials = get_assume_role_credentials(event["executionRoleArn"]) return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken'] ) def get_assume_role_credentials(role_arn): sts_client = boto3.client('sts') try: assume_role_response = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="configLambdaExecution") return assume_role_response['Credentials'] except botocore.exceptions.ClientError as ex: # Scrub error message for any internal account info leaks if 'AccessDenied' in ex.response['Error']['Code']: ex.response['Error']['Message'] = "AWS Config does not have permission to assume the IAM role." else: ex.response['Error']['Message'] = "InternalError" ex.response['Error']['Code'] = "InternalError" raise ex # Check whether the message is a ScheduledNotification or not. def is_scheduled_notification(message_type): return message_type == 'ScheduledNotification' def count_resource_types(applicable_resource_type, next_token, count): resource_identifier = AWS_CONFIG_CLIENT.list_discovered_resources(resourceType=applicable_resource_type, nextToken=next_token) updated = count + len(resource_identifier['resourceIdentifiers']); return updated # Evaluates the configuration items in the snapshot and returns the compliance value to the handler. def evaluate_compliance(max_count, actual_count): return 'NON_COMPLIANT' if int(actual_count) > int(max_count) else 'COMPLIANT' def evaluate_parameters(rule_parameters): if 'applicableResourceType' not in rule_parameters: raise ValueError('The parameter with "applicableResourceType" as key must be defined.') if not rule_parameters['applicableResourceType']: raise ValueError('The parameter "applicableResourceType" must have a defined value.') return rule_parameters # This generate an evaluation for config def build_evaluation(resource_id, compliance_type, event, resource_type=DEFAULT_RESOURCE_TYPE, annotation=None): """Form an evaluation as a dictionary. Usually suited to report on scheduled rules. Keyword arguments: resource_id -- the unique id of the resource to report compliance_type -- either COMPLIANT, NON_COMPLIANT or NOT_APPLICABLE event -- the event variable given in the lambda handler resource_type -- the CloudFormation resource type (or AWS::::Account) to report on the rule (default DEFAULT_RESOURCE_TYPE) annotation -- an annotation to be added to the evaluation (default None) """ eval_cc = {} if annotation: eval_cc['Annotation'] = annotation eval_cc['ComplianceResourceType'] = resource_type eval_cc['ComplianceResourceId'] = resource_id eval_cc['ComplianceType'] = compliance_type eval_cc['OrderingTimestamp'] = str(json.loads(event['invokingEvent'])['notificationCreationTime']) return eval_cc def lambda_handler(event, context): global AWS_CONFIG_CLIENT evaluations = [] rule_parameters = {} resource_count = 0 max_count = 0 invoking_event = json.loads(event['invokingEvent']) if 'ruleParameters' in event: rule_parameters = json.loads(event['ruleParameters']) valid_rule_parameters = evaluate_parameters(rule_parameters) compliance_value = 'NOT_APPLICABLE' AWS_CONFIG_CLIENT = get_client('config', event) if is_scheduled_notification(invoking_event['messageType']): result_resource_count = count_resource_types(valid_rule_parameters['applicableResourceType'], '', resource_count) if valid_rule_parameters.get('maxCount'): max_count = valid_rule_parameters['maxCount'] compliance_value = evaluate_compliance(max_count, result_resource_count) evaluations.append(build_evaluation(event['accountId'], compliance_value, event, resource_type=DEFAULT_RESOURCE_TYPE)) response = AWS_CONFIG_CLIENT.put_evaluations(Evaluations=evaluations, ResultToken=event['resultToken'])
函数运作

本函数在运行时执行以下操作:

  1. 当将event对象 Amazon Lambda 传递给函数时,该handler函数就会运行。在此示例中,该函数接受可选callback参数,该参数用于向调用者返回信息。 Amazon Lambda 还会传递一个context对象,其中包含函数在运行时可以使用的信息和方法。请注意,在新版本的 Lambda 中,不再使用上下文。

  2. 为计数指定类型的资源,处理程序会调用 countResourceTypes 函数,而且它传递其从事件收到的 applicableResourceType 参数。countResourceTypes 函数调用 listDiscoveredResources 客户端的 config 方法,该方法返回适用资源的标识符列表。该函数使用此列表的长度来确定适用资源的数量,而且它将此计数返回到处理程序。

  3. 处理程序准备 Amazon Config 通过初始化putEvaluationsRequest对象将评估结果发送到。此对象包括Evaluations参数,该参数标识合规性结果和 Amazon Web Services 账户 在事件中发布的结果。您可以使用 Evaluations 参数将结果应用于 Amazon Config支持的任何资源类型。该putEvaluationsRequest对象还包括事件的结果标记,用于标识规则和事件 Amazon Config。

  4. putEvaluationsRequest 对象中,处理程序调用 evaluateCompliance 函数。此函数测试适用资源的数量是否超出分配给事件所提供的 maxCount 参数的最大值。如果资源的数量超出最大值,函数将返回 NON_COMPLIANT。如果资源的数量没有超出最大值,函数将返回 COMPLIANT

  5. Amazon Config 通过将对象传递给config客户端putEvaluations的方法,处理程序将评估结果发送到。

当规则的触发器发生时,通过发布事件来 Amazon Config 调用规则的 Amazon Lambda 函数。然后通过将事件传递给函数的处理程序来 Amazon Lambda 执行该函数。

Example Event for Evaluations Triggered by Configuration Changes

Amazon Config 当它检测到规则范围内的资源的配置更改时会发布事件。以下示例事件显示该规则是由 EC2 实例的配置更改触发的。

{ "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-02-17T01:36:34.043Z\",\"awsAccountId\":\"123456789012\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"ARN\":\"arn:aws:ec2:us-east-2:123456789012:instance/i-00000000\",\"awsRegion\":\"us-east-2\",\"availabilityZone\":\"us-east-2a\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"Foo\":\"Bar\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"foo\":\"bar\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}", "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", "resultToken": "myResultToken", "eventLeftScope": false, "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", "configRuleArn": "arn:aws:config:us-east-2:123456789012:config-rule/config-rule-0123456", "configRuleName": "change-triggered-config-rule", "configRuleId": "config-rule-0123456", "accountId": "123456789012", "version": "1.0" }
Example Event for Evaluations Triggered by Oversized Configuration Changes

某些资源更改会生成过大配置项。以下示例事件显示该规则是由 EC2实例的超大配置更改触发的。

{ "invokingEvent": "{\"configurationItemSummary\": {\"changeType\": \"UPDATE\",\"configurationItemVersion\": \"1.2\",\"configurationItemCaptureTime\":\"2016-10-06T16:46:16.261Z\",\"configurationStateId\": 0,\"awsAccountId\":\"123456789012\",\"configurationItemStatus\": \"OK\",\"resourceType\": \"AWS::EC2::Instance\",\"resourceId\":\"i-00000000\",\"resourceName\":null,\"ARN\":\"arn:aws:ec2:us-west-2:123456789012:instance/i-00000000\",\"awsRegion\": \"us-west-2\",\"availabilityZone\":\"us-west-2a\",\"configurationStateMd5Hash\":\"8f1ee69b287895a0f8bc5753eca68e96\",\"resourceCreationTime\":\"2016-10-06T16:46:10.489Z\"},\"messageType\":\"OversizedConfigurationItemChangeNotification\"}", "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", "resultToken": "myResultToken", "eventLeftScope": false, "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", "configRuleArn": "arn:aws:config:us-east-2:123456789012:config-rule/config-rule-ec2-managed-instance-inventory", "configRuleName": "change-triggered-config-rule", "configRuleId": "config-rule-0123456", "accountId": "123456789012", "version": "1.0" }
Example Event for Evaluations Triggered by Periodic Frequency

Amazon Config 当它以您指定的频率(例如每 24 小时)评估您的资源时发布一个事件。下面的示例事件演示规则被定期频率触发。

{ "invokingEvent": "{\"awsAccountId\":\"123456789012\",\"notificationCreationTime\":\"2016-07-13T21:50:00.373Z\",\"messageType\":\"ScheduledNotification\",\"recordVersion\":\"1.0\"}", "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", "resultToken": "myResultToken", "eventLeftScope": false, "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", "configRuleArn": "arn:aws:config:us-east-2:123456789012:config-rule/config-rule-0123456", "configRuleName": "periodic-config-rule", "configRuleId": "config-rule-6543210", "accountId": "123456789012", "version": "1.0" }

事件属性

Amazon Config 事件的 JSON 对象包含以下属性:

invokingEvent

触发规则评估的事件。如果事件是为了响应资源配置更改而发布的,则此属性的值是一个包含 JSON configurationItemconfigurationItemSummary (对于过大配置项) 的字符串。配置项目表示 Amazon Config 检测到更改时资源的状态。有关配置项目的示例,请参阅中的get-resource-config-history Amazon CLI 命令生成的输出查看配置历史记录

如果事件是针对定期评估而发布的,则值是一个包含 JSON 对象的字符串。该对象包含关于已触发的评估的信息。

对于每种类型的事件,函数必须通过 JSON 解析程序解析字符串,以便能够评估其内容,如下面的 Node.js 示例中所示:

var invokingEvent = JSON.parse(event.invokingEvent);
ruleParameters

函数会将其作为评估逻辑的一部分来处理的键/值对。您可以在使用 Amazon Config 控制台创建自定义 Lambda 规则时定义参数。您也可以在 PutConfigRule Amazon Config API 请求或put-config-rule Amazon CLI 命令中使用InputParameters属性定义参数。

参数的 JSON 代码包含在字符串中,因此,函数必须通过 JSON 解析程序解析字符串,以便能够评估其内容,如下面的 Node.js 示例中所示:

var ruleParameters = JSON.parse(event.ruleParameters);
resultToken

函数在调用时必须传递给 Amazon Config 的标PutEvaluations记。

eventLeftScope

一个布尔值,表示要评估的 Amazon 资源是否已从规则的作用域中移除。如果值为 true,则该函数表示可通过传递 NOT_APPLICABLE 作为 ComplianceType 调用中的 PutEvaluations 属性值来忽略评估。

executionRoleArn

分配给的 IAM 角色的 ARN。 Amazon Config

configRuleArn

Amazon Config 分配给规则的 ARN。

configRuleName

您为导致 Amazon Config 发布事件和调用函数的规则分配的名称。

configRuleId

Amazon Config 分配给规则的 ID。

accountId

拥有 Amazon Web Services 账户 该规则的 ID。

version

由分配的版本号 Amazon。如果向 Amazon Config 事件 Amazon 添加属性,则版本将增加。如果函数需要仅在匹配或超过特定版本的事件中的属性,则该函数可以检查此属性的值。

Amazon Config 事件的当前版本是 1.0。