AWS Config 규칙의 AWS Lambda 함수 예제(Node.js) - AWS Config

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

AWS Config 규칙의 AWS Lambda 함수 예제(Node.js)

AWS Lambda는 AWS 서비스에서 게시한 이벤트에 대한 응답으로 함수를 실행합니다. AWS ConfigCustom Lambda 규칙 함수는 에서 AWS Config 게시한 이벤트를 수신하고, 함수는 이벤트에서 수신하고 API에서 검색한 데이터를 사용하여 규칙 규정 AWS Config 준수를 평가합니다. 구성 규칙의 함수 작업은 구성 변경으로 트리거된 평가를 수행하는지 주기적으로 트리거된 평가를 수행하는지에 따라 다릅니다.

AWS Lambda함수 내의 일반적인 패턴에 대한 자세한 내용은 개발자 안내서의 프로그래밍 모델을 참조하십시오. AWS Lambda

구성 변경으로 트리거되는 평가에 대한 함수 예제

AWS Config는 사용자 지정 규칙의 범위 내에 있는 리소스의 구성 변경을 발견하면 다음 예제와 같은 함수를 호출합니다.

AWS Config 콘솔을 사용하여 이 예제와 같은 함수와 연결된 규칙을 만드는 경우, 트리거 유형으로 구성 변경을 선택합니다. AWS Config API 또는 AWS CLI를 사용하여 규칙을 만드는 경우, MessageType 속성을 ConfigurationItemChangeNotificationOversizedConfigurationItemChangeNotification으로 설정합니다. 이러한 설정을 통해 AWS Config가 리소스 변경의 결과로 구성 항목 또는 크기를 초과한 구성 항목을 생성할 때마다 규칙을 트리거할 수 있습니다.

이 예제에서는 리소스를 평가하고 인스턴스가 리소스 유형 AWS::EC2::Instance와 일치하는지 확인합니다. AWS Config가 구성 항목 또는 크기를 초과한 구성 항목 알림을 생성하면 규칙이 트리거됩니다.

'use strict'; const aws = require('aws-sdk'); const config = new aws.ConfigService(); // 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. function getConfiguration(resourceType, resourceId, configurationCaptureTime, callback) { config.getResourceConfigHistory({ resourceType, resourceId, laterTime: new Date(configurationCaptureTime), limit: 1 }, (err, data) => { if (err) { callback(err, null); } const configurationItem = data.configurationItems[0]; callback(null, configurationItem); }); } // 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. function getConfigurationItem(invokingEvent, callback) { checkDefined(invokingEvent, 'invokingEvent'); if (isOverSizedChangeNotification(invokingEvent.messageType)) { const configurationItemSummary = checkDefined(invokingEvent.configurationItemSummary, 'configurationItemSummary'); 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. exports.handler = (event, context, callback) => { checkDefined(event, 'event'); const invokingEvent = JSON.parse(event.invokingEvent); const ruleParameters = JSON.parse(event.ruleParameters); getConfigurationItem(invokingEvent, (err, configurationItem) => { if (err) { callback(err); } let compliance = 'NOT_APPLICABLE'; const putEvaluationsRequest = {}; if (isApplicable(configurationItem, event)) { // Invoke the compliance checking function. compliance = evaluateChangeNotificationCompliance(configurationItem, ruleParameters); } // Initializes the request that contains the evaluation results. putEvaluationsRequest.Evaluations = [ { ComplianceResourceType: configurationItem.resourceType, ComplianceResourceId: configurationItem.resourceId, ComplianceType: compliance, OrderingTimestamp: configurationItem.configurationItemCaptureTime, }, ]; putEvaluationsRequest.ResultToken = event.resultToken; // Sends the evaluation results to AWS Config. config.putEvaluations(putEvaluationsRequest, (error, data) => { if (error) { callback(error, null); } else if (data.FailedEvaluations.length > 0) { // Ends the function if evaluation results are not successfully reported to AWS Config. callback(JSON.stringify(data), null); } else { callback(null, data); } }); }); };
함수 작업

함수는 실행 시 다음 작업을 수행합니다.

  1. 함수는 event 객체를 함수에 AWS Lambda 전달할 때 실행됩니다. handler 이 예제에서 함수는 호출자에게 정보를 반환하는 데 사용하는 선택적 callback 매개 변수를 받습니다. AWS Lambda또한 context 객체를 전달하는데, 이 객체에는 함수가 실행되는 동안 사용할 수 있는 정보와 메서드가 들어 있습니다. 참고로 최신 버전의 Lambda에서는 컨텍스트가 더 이상 사용되지 않습니다.

  2. 함수는 이벤트의 messageType이 구성 항목인지 크기를 초과한 구성 항목인지 확인한 다음, 해당 구성 항목을 반환합니다.

  3. 핸들러는 isApplicable 함수를 호출하여 리소스가 삭제되었는지 여부를 확인합니다.

    참고

    삭제된 리소스에 대한 규칙 보고는 불필요한 규칙 평가를 피하기 위해 평가 결과를 반환해야 합니다. NOT_APPLICABLE

  4. 핸들러는 evaluateChangeNotificationCompliance 함수를 호출하고, AWS Config가 이벤트에 게시한 configurationItemruleParameters 객체를 전달합니다.

    함수는 먼저 리소스가 EC2 인스턴스인지 여부를 평가합니다. 리소스가 EC2 인스턴스가 아닌 경우, 함수는 준수 값 NOT_APPLICABLE을 반환합니다.

    그런 다음 함수는 구성 항목의 instanceType 속성이 desiredInstanceType 파라미터 값과 같은지 여부를 평가합니다. 값이 같으면 함수는 COMPLIANT를 반환합니다. 값이 같지 않으면 함수는 NON_COMPLIANT를 반환합니다.

  5. 핸들러는 putEvaluationsRequest 객체를 초기화함으로써 AWS Config로 평가 결과를 보낼 준비를 합니다. 이 객체에는 준수 결과를 식별하는 Evaluations 파라미터, 리소스 유형, 평가된 리소스의 ID가 포함되어 있습니다. 또한 putEvaluationsRequest 객체에는 AWS Config의 규칙 및 이벤트를 식별하는 이벤트의 결과 토큰이 포함되어 있습니다.

  6. 핸들러는 이 객체를 config 클라이언트의 putEvaluations 메서드로 전달하여 AWS Config에 평과 결과를 보냅니다.

주기적 평가에 대한 함수 예제

AWS Config는 주기적 평가에 대해 다음 예제와 같은 함수를 호출합니다. 주기적 평가는 사용자가 AWS Config에서 규칙을 정의할 때 지정한 간격으로 발생합니다.

AWS Config 콘솔을 사용하여 이 예제와 같은 함수와 연결된 규칙을 만드는 경우, 트리거 유형으로 주기적을 선택합니다. AWS Config API 또는 AWS CLI를 사용하여 규칙을 만드는 경우, MessageType 속성을 ScheduledNotification으로 설정합니다.

이 예제에서는 지정한 총 리소스 수가 지정한 최대치를 초과하는지 확인합니다.

var aws = require('aws-sdk'), // Loads the AWS SDK for JavaScript. config = new aws.ConfigService(), // Constructs a service object to use the aws.ConfigService class. COMPLIANCE_STATES = { COMPLIANT : 'COMPLIANT', NON_COMPLIANT : 'NON_COMPLIANT', NOT_APPLICABLE : 'NOT_APPLICABLE' }; // Receives the event and context from AWS Lambda. exports.handler = function(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), noOfResources = 0; if (isScheduledNotification(invokingEvent)) { countResourceTypes(ruleParameters.applicableResourceType, "", noOfResources, function(err, count) { if (err === null) { var putEvaluationsRequest; // Initializes the request that contains the evaluation results. putEvaluationsRequest = { Evaluations : [ { // Applies the evaluation result to the AWS account published in the event. ComplianceResourceType : 'AWS::::Account', ComplianceResourceId : event.accountId, ComplianceType : evaluateCompliance(ruleParameters.maxCount, count), OrderingTimestamp : new Date() } ], ResultToken : event.resultToken }; // Sends the evaluation results to AWS Config. config.putEvaluations(putEvaluationsRequest, function(err, data) { if (err) { callback(err, null); } else { if (data.FailedEvaluations.length > 0) { // Ends the function execution if evaluation results are not successfully reported callback(JSON.stringify(data)); } callback(null, data); } }); } 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 whether the compliance conditions for the rule are violated. function evaluateCompliance(maxCount, actualCount) { if (actualCount > maxCount) { return COMPLIANCE_STATES.NON_COMPLIANT; } else { return COMPLIANCE_STATES.COMPLIANT; } } // Counts the applicable resources that belong to the AWS account. function countResourceTypes(applicableResourceType, nextToken, count, callback) { config.listDiscoveredResources({resourceType : applicableResourceType, nextToken : nextToken}, function(err, data) { if (err) { callback(err, null); } else { count = count + data.resourceIdentifiers.length; if (data.nextToken !== undefined && data.nextToken != null) { countResourceTypes(applicableResourceType, data.nextToken, count, callback); } callback(null, count); } }); return count; }
함수 작업

함수는 실행 시 다음 작업을 수행합니다.

  1. event객체를 함수에 AWS Lambda 전달하면 handler 함수가 실행됩니다. 이 예제에서 함수는 호출자에게 정보를 반환하는 데 사용하는 선택적 callback 매개 변수를 받습니다. AWS Lambda또한 context 객체를 전달하는데, 이 객체에는 함수가 실행되는 동안 사용할 수 있는 정보와 메서드가 들어 있습니다. 참고로 최신 버전의 Lambda에서는 컨텍스트가 더 이상 사용되지 않습니다.

  2. 지정된 유형의 리소스 수를 세기 위해 핸들러는 countResourceTypes 함수를 호출한 다음, 이벤트에서 수신한 applicableResourceType 파라미터를 전달합니다. countResourceTypes 함수는 listDiscoveredResources 클라이언트의 config 메서드를 호출합니다. 그러면 해당하는 리소스의 식별자 목록이 반환됩니다. 함수는 이 목록의 길이를 사용하여 해당하는 리소스 수를 확인하고, 이 수를 핸들러에게 반환합니다.

  3. 핸들러는 putEvaluationsRequest 객체를 초기화함으로써 AWS Config로 평가 결과를 보낼 준비를 합니다. 이 객체에는 규정 준수 결과와 이벤트에 게시된 결과를 식별하는 Evaluations 파라미터가 포함됩니다. AWS 계정 Evaluations 파라미터를 사용하여 AWS Config가 지원하는 리소스 유형에 결과를 적용할 수 있습니다. 또한 putEvaluationsRequest 객체에는 AWS Config의 규칙 및 이벤트를 식별하는 이벤트의 결과 토큰이 포함되어 있습니다.

  4. putEvaluationsRequest 객체 내에서 핸들러는 evaluateCompliance 함수를 호출합니다. 이 함수는 해당하는 리소스 수가 이벤트에서 제공한 maxCount 파라미터에 할당된 최대치를 초과하는지 여부를 테스트합니다. 리소스 수가 최대치를 초과하면 함수는 NON_COMPLIANT를 반환합니다. 리소스 수가 최대치를 초과하지 않으면 함수는 COMPLIANT를 반환합니다.

  5. 핸들러는 이 객체를 config 클라이언트의 putEvaluations 메서드로 전달하여 AWS Config에 평과 결과를 보냅니다.