AWS Config Rulesのカスタムルール(Lambda連携)を触ってみた #reinvent

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

西澤です。先日のre:Invent 2015で発表されたAWS Config RulesPreview申し込みも通ったので、ドキュメントで紹介されていたサンプルを試してみました。下記記事もぜひ合わせてご覧ください。

やってみた内容

下記ドキュメントにサンプルが紹介されていましたので、まずはこの通りやってみました。注意事項などを整理しながらご紹介したいと思います。

Getting Started with Custom Rules (Node.js)

  1. カスタムルール用のLambdaファンクションを作成
  2. AWS Configにカスタムルールを作成

カスタムルール用のLambdaファンクションを作成

 サンプルコード

まずは、下記ページからzipファイルをそのままダウンロードしてきます。

中に謎のjsonファイルが含まれています。どうやらAWS Config APIを利用する為に必要となるLibrary情報等が定義されている必須のファイルのようで、zipに含めてアップロードする必要があります。今後SDKがバージョンアップされれば不要となるのかもしれません。

InstanceTypeCheck.jsが、Lambdaから実行される実体のコードですので、こちらを覗いてみます。

var aws  = require('aws-sdk');
// Include the Pre-release SDK explicitly. Once this is released into the AWS SDK, this step is not required
var config = new aws.Service({
    apiConfig: require('./config-2014-11-12.normal.json'),
    region: 'us-east-1'
}); 

// This is where its determined whether the resource is compliant or not. 
// In this example, we simply decide that the resource is compliant if is an Instance and its type matches the type specified as the desired type
// If the resource is not an Instance, then we deem this resource to not be applicable (if the scope of the rule is specified to only include 
// Instances, this rule would never have been invoked )

function evaluateCompliance(configurationItem, ruleParameters) {
    if(configurationItem.resourceType !== 'AWS::EC2::Instance')
        return 'NOT_APPLICABLE';
    if(configurationItem.configuration.instanceType === ruleParameters.desiredInstanceType)
        return 'COMPLIANT';
    else return 'NON_COMPLIANT';
}


// Check whether the we're being invoked for this resource because its been deleted. If it has, then its not applicable to be evaluated

function isApplicable(configurationItem) {
    var status = configurationItem.configurationItemStatus;
    return status === 'OK' || status === 'ResourceDiscovered';
}


// This is the handler that's invoked by Lambda
// Most of this code is boiler-plate, use as is 

exports.handler = function(event, context) {
    var invokingEvent = JSON.parse(event.invokingEvent);
    var ruleParameters = JSON.parse(event.ruleParameters);
    var compliance = 'NOT_APPLICABLE';
    
    if (isApplicable(invokingEvent.configurationItem))
        compliance = evaluateCompliance(invokingEvent.configurationItem, ruleParameters, context); // Invoke the compliance checking function.

    // Put together the request that reports the evaluation status
    // Note that we're choosing to report this evaluation against the resource that was passed in.
    // You can choose to report this against any other resource type, as long as it is supported by ConfigRules

    var putEvaluationsRequest = {
       Evaluations: [
           {
               ComplianceResourceType: invokingEvent.configurationItem.resourceType,
               ComplianceResourceId: invokingEvent.configurationItem.resourceId,
               ComplianceType: compliance,
               OrderingTimestamp: invokingEvent.configurationItem.configurationItemCaptureTime
           }
       ],
       ResultToken: event.resultToken
    };

    // Invoke the Config API to report the result of the evaluation
    config.putEvaluations(putEvaluationsRequest, function (err, data) {
        if (err) {
            context.fail(err);
        } else {
            context.succeed(data);
        }
    });
};

カスタムルールを開発していく上で、共通部分となるところはconfig.putEvaluationsで、各設定がcompliant(ルールに即した設定となっている)かどうかを判定した結果を返す処理になります。今回は、EC2インスタンスのインスタンスタイプがcompliantであるかどうかを判定するサンプルになります。

Lambdaファンクション作成

ドキュメントの通りにLambdaファンクションを作成します。

awsconfig_lambda1

Lambda実行用IAMロール作成

Lambdaファンクション作成の際に、割り当てるIAMロールを作成します(事前作成も可能ですが)。通常のLambda用に最低限定義されている"Basic execution role"に加えて、config:PutEvaluationsの権限が追加で必要となる点にご注意ください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "config:PutEvaluations"
      ],
      "Resource": "*"
    }
  ]
}

Lambdaの実行テスト

ドキュメントでは、Input sample eventも用意されていました。自分で開発したときは、これを用意するのがなかなか大変だったりしたので、なんて親切なんだ!と思って実行してみたのですが。。。

awsconfig_lambda2

真っ赤な文字だらけ。AWS Configから実行された場合だけ成功するので、InvalidResultTokenExceptionで良いそうです。"not authorized to perform: config:PutEvaluations."が出たら、権限不足とのことなので、これを信じて先に進みましょう。

awsconfig_lambda3

LambdaファンクションへのAWS Configからの実行権限付与

Lambdaファンクションにリソースベースで権限を付ける必要があるとのこと。AWS Management Consoleでは見当たらなかったので、Lambdaファンクションに権限を付けて制御できることは、ここで初めて知りました。AWS ConfigがこのLambdaファンクションを実行できるように設定しておきます。

$ aws lambda add-permission \
  --function-name InstanceTypeCheck \
  --statement-id "AddConfigPermission" \
  --action lambda:InvokeFunction \
  --principal config.amazonaws.com
{ "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-1:<AWS ACCOUNT ID>:function:InstanceTypeCheck\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"config.amazonaws.com\"},\"Sid\":\"AddConfigPermission\"}],\"Id\":\"default\"}" }

$ aws lambda get-policy \
  --function-name InstanceTypeCheck
{ "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-east-1:<AWS ACCOUNT ID>:function:InstanceTypeCheck\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"config.amazonaws.com\"},\"Sid\":\"AddConfigPermission\"}],\"Id\":\"default\"}" }

AWS Configにカスタムルールを作成

こちらもドキュメントの通りにやってみましょう。

awsconfig_lambda4

ここで、Lambdaに登録したスクリプト内にある外部パラメータを指定して、判定を行っています。

awsconfig_lambda5

  • Key=desiredInstanceType
  • Value=t2.micro

AWS Configの作成はとてもシンプルですね。

テスト

いよいよテストです。試しに、t2.microとt2.smallをそれぞれ1台ずつ起動してみました。

awsconfig_lambda_result1

awsconfig_lambda_result2

1台が"Compliant"、もう1台が"Noncopliant"で画面表示されました!

まとめ

今回はサンプルを利用してAWS Config Rulesのカスタムルールを試してみました。CloudTrailとごちゃ混ぜになってしまっていたのですが、AWS Configの武器は過去の状態を追跡できること、ということを再認識しました。あとLambdaももっとトレーニングを積まないといけないですね。

今後もAWS Configの特徴を生かしたカスタムルールを考えて行きたいと思います。AWS Config Rulesの正式リリースが待たれます。