

# AWS Config を使用して Lambda の予防的コントロールを実装する
<a name="governance-config"></a>

開発プロセスのできるだけ早い段階で、サーバーレスアプリケーションのコンプライアンスを確保することが不可欠です。このトピックでは、[AWS Config](https://docs.aws.amazon.com/config/latest/developerguide/WhatIsConfig.html) を使用して予防的制御を実装する方法について説明します。これにより、開発プロセスの早い段階でコンプライアンスチェックを実装でき、CI/CD パイプラインにも同じコントロールを実装できます。また、一元管理されるルールのリポジトリでコントロールが標準化され、AWS アカウント全体にコントロールを一貫して適用できるようになります。

例えば、コンプライアンス管理者が、すべての Lambda 関数に AWS X-Ray トレースが含まれるようにする要件を定義したとします。AWS Config のプロアクティブモードを使用すると、デプロイ前に Lambda 関数リソースのコンプライアンスチェックを実行できます。不適切に設定された Lambda 関数をデプロイするリスクを軽減し、インフラストラクチャに関するフィードバックをコードテンプレートとして迅速に提供することで、開発者の時間を節約できます。以下は AWS Config による予防コントロールのフローを視覚化したものです。

 ![\[CloudFormation requests must pass AWS Config rules before provisioning.\]](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images/governance-config-1.png) 

すべての Lambda 関数でトレースを有効にする必要があるという要件を考えてみましょう。これを受けて、プラットフォームチームは、特定の AWS Config ルールをすべてのアカウントでプロアクティブに実行する必要があると判断しました。このルールは、X-Ray トレーシング設定が設定されていない Lambda 関数を非準拠リソースとしてフラグします。チームはルールを作成し、それを[コンフォーマンスパック](https://docs.aws.amazon.com/config/latest/developerguide/conformance-packs.html)にパッケージ化し、そのコンフォーマンスパックをすべての AWS アカウントにデプロイして、組織内のすべてのアカウントがこれらのコントロールを統一的に適用できるようにします。ルールは AWS CloudFormation Guard 2.x.x 構文で記述し、次のような形式を取ります。

```
rule name when condition { assertion }
```

以下は、Lambda 関数でトレースが有効になっていることを確認する Guard ルールのサンプルです。

```
rule lambda_tracing_check {
  when configuration.tracingConfig exists {
      configuration.tracingConfig.mode == "Active"
  }
}
```

 プラットフォームチームは、すべての AWS CloudFormation デプロイで事前作成/更新[フック](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/hooks-structure.html)を呼び出すことを義務付けることで、さらなる措置を講じます。また、このフックを開発してパイプラインを構成し、コンプライアンスルールの一元管理を強化し、すべてのデプロイメントで一貫した適用を維持する全責任を負います。フックを開発、パッケージ化、登録するには、CloudFormation コマンドラインインターフェイス (CFN-CLI) ドキュメントの「[Developing AWS CloudFormation Hooks](https://docs.aws.amazon.com/cloudformation-cli/latest/hooks-userguide/hooks-develop.html)」を参照してください。[CloudFormation CLI](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/initiating-hooks-project-python.html) を使用してフックプロジェクトを作成できます。

```
cfn init
```

このコマンドは、フックプロジェクトに関する基本情報の入力を求め、以下のファイルを含むプロジェクトを作成します。

```
README.md
<hook-name>.json
rpdk.log
src/handler.py
template.yml
hook-role.yaml
```

フック開発者は、必要なターゲットリソースタイプを `<hook-name>.json` 設定ファイルに追加する必要があります。以下の設定では、CloudFormation を使用して Lambda 関数が作成される前にフックが実行されるように設定されています。`preUpdate` および `preDelete` アクションにも同様のハンドラーを追加できます。

```
    "handlers": {
        "preCreate": {
            "targetNames": [
                "AWS::Lambda::Function"
            ],
            "permissions": []
        }
    }
```

また、CloudFormation フックに AWS Config API を呼び出すための適切な権限があることを確認する必要があります。そのためには、`hook-role.yaml` という名前のロール定義ファイルを更新します。ロール定義ファイルには、デフォルトで以下の信頼ポリシーがあります。これにより、CloudFormation がロールを引き受けることができます。

```
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - hooks.cloudformation.amazonaws.com
                - resources.cloudformation.amazonaws.com
```

このフックで config API を呼び出せるようにするには、ポリシーステートメントに以下の権限を追加する必要があります。次に、`cfn submit` コマンドを使用してフックプロジェクトを送信します。ここで、CloudFormation は必要な権限を持つロールを作成します。

```
      Policies:
        - PolicyName: HookTypePolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - "config:Describe*"
                  - "config:Get*"
                  - "config:List*"
                  - "config:SelectResourceConfig"
                Resource: "*
```

次に、Lambda 関数を `src/handler.py` ファイルに記述する必要があります。このファイルには `preCreate`、`preUpdate`、`preDelete` という名前のメソッドがあり、プロジェクトを開始したときにすでに作成されています。目的は、AWS SDK for Python (Boto3) を使用してプロアクティブモードで AWS Config `StartResourceEvaluation` API を呼び出す、再利用可能な共通関数を作成することです。この API コールは、リソースプロパティを入力として受け取り、ルール定義と照らし合わせてリソースを評価します。

```
def validate_lambda_tracing_config(resource_type, function_properties: MutableMapping[str, Any]) -> ProgressEvent:
  LOG.info("Fetching proactive data")
  config_client = boto3.client('config')
  resource_specs = {
      'ResourceId': 'MyFunction',
      'ResourceType': resource_type,
      'ResourceConfiguration': json.dumps(function_properties),
      'ResourceConfigurationSchemaType': 'CFN_RESOURCE_SCHEMA'
  }
  LOG.info("Resource Specifications:", resource_specs)
  eval_response = config_client.start_resource_evaluation(EvaluationMode='PROACTIVE', ResourceDetails=resource_specs, EvaluationTimeout=60)
  ResourceEvaluationId = eval_response.ResourceEvaluationId
  compliance_response = config_client.get_compliance_details_by_resource(ResourceEvaluationId=ResourceEvaluationId)
  LOG.info("Compliance Verification:", compliance_response.EvaluationResults[0].ComplianceType)
  if "NON_COMPLIANT" == compliance_response.EvaluationResults[0].ComplianceType:
      return ProgressEvent(status=OperationStatus.FAILED, message="Lambda function found with no tracing enabled : FAILED", errorCode=HandlerErrorCode.NonCompliant)
  else:
      return ProgressEvent(status=OperationStatus.SUCCESS, message="Lambda function found with tracing enabled : PASS.")
```

これで、作成前フックのハンドラーから共通関数を呼び出すことができます。ハンドラーの例を示します。

```
@hook.handler(HookInvocationPoint.CREATE_PRE_PROVISION)
def pre_create_handler(
        session: Optional[SessionProxy],
        request: HookHandlerRequest,
        callback_context: MutableMapping[str, Any],
        type_configuration: TypeConfigurationModel
) -> ProgressEvent:
    LOG.info("Starting execution of the hook")
    target_name = request.hookContext.targetName
    LOG.info("Target Name:", target_name)
    if "AWS::Lambda::Function" == target_name:
        return validate_lambda_tracing_config(target_name,
            request.hookContext.targetModel.get("resourceProperties")
        )
    else:
        raise exceptions.InvalidRequest(f"Unknown target type: {target_name}")
```

このステップの後、フックを登録して、すべての AWS Lambda 関数作成イベントをリッスンするように設定できます。

 開発者は、Lambda を使用してサーバーレスマイクロサービス用の Infrastructure as Code (IaC) テンプレートを準備します。この準備には、内部標準を順守した後に、テンプレートをローカルでテストしてリポジトリにコミットすることが含まれます。IaC テンプレート例を次に示します。

```
  MyLambdaFunction:
  Type: 'AWS::Lambda::Function'
  Properties:
    Handler: index.handler
    Role: !GetAtt LambdaExecutionRole.Arn
    FunctionName: MyLambdaFunction
    Code:
      ZipFile: |
        import json

        def handler(event, context):
            return {
                'statusCode': 200,
                'body': json.dumps('Hello World!')
            }
    Runtime: python3.14
    TracingConfig:
        Mode: PassThrough
    MemorySize: 256
    Timeout: 10
```

CI/CD プロセスの一環として、CloudFormation テンプレートがデプロイされると、CloudFormation サービスは `AWS::Lambda::Function` リソースタイプをプロビジョニングする直前に事前作成/更新フックを呼び出します。フックは、プロアクティブモードで実行されている AWS Config ルールを利用して、Lambda 関数設定に必須のトレース設定が含まれていることを確認します。フックからの応答によって次のステップが決まります。準拠していれば、フックは成功を通知し、CloudFormation はリソースのプロビジョニングを続行します。そうでない場合、CloudFormation スタックのデプロイは失敗し、パイプラインはただちに停止し、システムはその詳細を記録して後で確認できるようにします。コンプライアンス通知は、関連する利害関係者に送信されます。

フックの成功/失敗に関する情報は、CloudFormation コンソールで確認できます。

 ![\[Hook success/fail information in the CloudFormation console\]](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images/governance-config-2.png) 

CloudFormation フックのログが有効になっている場合は、フックの評価結果をキャプチャできます。以下は、失敗ステータスのフックのサンプルログです。これは、Lambda 関数で X-Ray が有効になっていないことを示しています。

 ![\[Sample log for a hook with a failed status\]](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images/governance-config-3.png) 

開発者が IaC を変更して `TracingConfig Mode` 値を `Active` に更新して再デプロイすることを選択した場合、フックは正常に実行され、スタックは Lambda リソースの作成に進みます。

 ![\[CloudFormation console shows successful resource deployment\]](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images/governance-config-4.png) 

これにより、サーバーレスリソースを開発して AWS アカウントにデプロイするときに、AWS Config による予防的コントロールをプロアクティブモードで実装できます。AWS Config ルールを CI/CD パイプラインに統合することで、アクティブなトレーシング設定がない Lambda 関数など、非準拠のリソースのデプロイを特定し、オプションでブロックできます。これにより、最新のガバナンスポリシーに準拠するリソースのみが AWS 環境にデプロイされます。