14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS CDKを使ってTrend Micro Cloud One Container SecurityをEKSにセットアップしてみた その1

Posted at

背景

Trend Micro Cloud One Container Securityとは

Trend Micro Cloud One Container Securityとは、コンテナイメージのスキャンやポリシーベースのデプロイメントコントロール(例えば、イメージスキャンで脆弱性が見つかったイメージはデプロイできないなど)などの機能を兼ね備えたKubernetesに対するセキュリティソリューションです。

詳しくは公式サイトをご覧ください。

container_security_lifecycle.png
画像は公式ガイドより

AWS CDKとは

AWS CDKとは、TypeScriptやPythonなど使い慣れたプログラミング言語を使用してクラウドアプリケーションリソースを定義するためのオープンソースのソフトウェア開発フレームワークです。

いわゆるInfrastructure as Code(IaC)ツールの一種ですね。

こちらも詳しくは公式サイトをご覧ください。

AppStacks.png
画像は公式開発者ガイドより

なぜAWS CDKを使ってセットアップしたいのか

AWS CDKおよびInfrastructure as Codeのメリットは多岐に渡りますが、本記事ではセキュリティの向上に着目したいと思います。Cloud One Container Securityは確かに素晴らしい製品ですが、万能ではありません。せっかくCloud One Container SecurityでEKS Clusterをセキュアにしても、他のところが雑だとそこが脆弱性になってしまいます。

  • EKS Clusterが乗っているVPCレベルのセキュリティ(例えば、セキュリティグループの設定)が雑になっている
  • API KEYや認証情報(例えば、Clusterに乗っているコンテナが参照するRDSのID/Pass)の管理が雑になっている

AWS CDKを使うことでなぜ上記のような「雑な設定」を防ぐことができるのか、解説を交えながら手順を説明します。

Container SecurityにClusterを追加する

まず、Container Securityで管理する対象のEKS ClusterをAWS CDKで作成して、管理対象に追加します。

基本的には、公式ガイドのAdd a clusterに記載されている手順をAWS CDKベースで行なっていきます。

開発環境構築

まずは開発環境を作ります。ローカルPCに作ることもできますが、今回はAWS Cloud9を利用することにします。

ローカルPCからAWS CDKを実行する場合は、IAMのAccess Key IDやSecret Access Key IDの発行・管理がセキュリティ上の課題となります。
一方でCloud9の場合は、AWS Managed Temporary CredentialsもしくはEC2インスタンスに付与したIAMロールベースでの実行の利用が可能なので、Management Consoleへのログインをしっかり管理している前提(例えばMFAを必須にしているなど)であれば、セキュリティ上比較的安全に利用することができるかと思います。

Cloud9インスタンスの作成

Management ConsoleからCloud9のインスタンスを作成します。

スペックはサイフの中身と相談だとは思いますが、AWS CDKでEKSのプロジェクトを扱う(≒Cloud9の中でdocker buildなどを行う)前提で、快適に開発するための最低スペックは以下のような感じかなと思います。

インスタンスタイプ
m5.large (t3.smallでもいけなくないですがちょっと重いです。。)
  
プラットフォーム
Amazon Linux2 (ほかでも問題ないですが、まあこれが無難かと。)
EBSボリュームサイズ
40GB (Cloud9のコンソールからは変更できないですが、EC2のコンソールから変更ください。デフォルトの10GBはすぐ枯渇します)

Cloud9へのミドルウェアのインストール

私はAWS CDKはTypeScriptで書きたいので、その前提でミドルウェアを整えていきます。何度も同じ作業をやってきたので、シェルスクリプト化しました。

setup_cdk.sh
#!/bin/bash

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # nvmのロード

nvm install stable #nodeの最新Stableのインストール
nvm alias default stable #デフォルトで↑のStable版を使うよう設定
npm update -g npm #npmの最新化
npm install -g yarn #yarnのインストール
echo export PATH=\$PATH:`yarn global bin` >> ~/.bashrc  # yarnのglobalディレクトリにpathを通す
source ~/.bashrc #上でやった設定の反映

yarn global add aws-cdk # CDK Cliのインストール

CDKプロジェクトの作成

インストールしたAWS CDKのCliを使ってAWS CDKのプロジェクトを作成します。

$ mkdir cdk-container-security-sample
$ cd cdk-container-security-sample
$ cdk init --language=typescript # TypeScriptのCDKプロジェクトを作成する

スクリーンショット 2021-07-23 10.15.10.png

このようにプロジェクトが初期化されればOKです。

CDK Bootstrap (AWSアカウント/リージョンごとに初めてAWS CDKを使用する場合のみ)

初めてAWS CDKを利用する場合、CDK Bootstrapという初期設定が必要です。CDKの実行に必要なS3バケットや関連するIAMロールなどが作成されます。

# 初期化したcdk-container-security-sampleディレクトリ内で実行
$ yarn run cdk bootsrtap

Container Securityで管理するEKS Clusterを作成する

早速、lib/cdk-container-security-sample-stack.tsを編集して、Container Securityの監視対象にするAWS EKSのコンテナを作成します。ただVPCを作成してそこにEKSクラスタを作成するだけでも良いのですが、ついでにEKSに展開するコンテナ上のアプリケーションが利用する想定で、PostgreSQLのAWS RDSを同じVPCに作成します。

関連ライブラリの追加

$ yarn add @aws-cdk/aws-ec2 @aws-cdk/aws-eks @aws-cdk/aws-rds

EKSを構築するスタックの作成

lib/cdk-container-security-sample-stack.ts
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as eks from '@aws-cdk/aws-eks';
import * as rds from '@aws-cdk/aws-rds';

/**
 * 使用するCDK Contextをマップするinterface
 */
interface CdkContainerSecuritySampleContext{
  /** CIDRプレフィックス*/
  cidrPrefix: string,
  /** EKSクラスタのノード数*/
  desiredCapacity: number,
  /** EKSクラスタのノードのインスタンスタイプ*/
  nodeInstanceType: string,
  /** EKSクラスタ名*/
  clusterName: string,
}

/**
 * CdkContainerSecuritySampleContextのデフォルト値
 */ 
const DEFAULT_CONTEXT: CdkContainerSecuritySampleContext = {
  cidrPrefix: '10.0',
  desiredCapacity: 2,
  nodeInstanceType: 'm5.large',
  clusterName: 'container-security-sample',
}


export class CdkContainerSecuritySampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    // CDK Contextのパース
    const context = this.parseContext();
    
    // EKSクラスタをデプロイするVPCの作成
    const vpc = new ec2.Vpc(this, 'cloudone-poc-vpc', {
      cidr: `${context.cidrPrefix}.0.0/16`,
      natGateways:1,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: 'Public1',
          subnetType: ec2.SubnetType.PUBLIC
        },
        {
          cidrMask: 24,
          name: 'Public2',
          subnetType: ec2.SubnetType.PUBLIC
        },
        {
          cidrMask: 24,
          name: 'Private1',
          subnetType: ec2.SubnetType.PRIVATE
        },
        {
          cidrMask: 24,
          name: 'Private2',
          subnetType: ec2.SubnetType.PRIVATE
        }
      ]
    });
    
    // EKSクラスタの作成
    const cluster = new eks.Cluster(this, 'container-security-cluster', {
      vpc,
      clusterName: context.clusterName,
      version: eks.KubernetesVersion.V1_20,
      defaultCapacity: context.desiredCapacity,
      defaultCapacityInstance: new ec2.InstanceType(context.nodeInstanceType)
    });
    
    
    /**
     * kubernetes-external-secrets(AWS Secrets Managerで管理されているSecretをKubernatesのSecretに連携するController)のインストール
     */ 
     
    // kubernetes-external-secretsで使用するサービスアカウントの作成
    const externalSecretServiceAccount = cluster.addServiceAccount("external-secret-sa", {name: `${context.clusterName}-sa`});
    
    // Helm経由でkubernetes-external-secretsのインストール
    // @see https://github.com/external-secrets/kubernetes-external-secrets/tree/master/charts/kubernetes-external-secrets
    const externalSecretHelm = new eks.HelmChart(this,'external-secret', {
      cluster,
      repository: "https://external-secrets.github.io/kubernetes-external-secrets",
      chart: "kubernetes-external-secrets",
      values: {
        securityContext:{
          fsGroup: 65534
        },
        serviceAccount:{
          create:false,
          name: externalSecretServiceAccount.serviceAccountName
        },
        env:{
          AWS_REGION: this.region
        }
      }
    });
    
    
    // EKS上のコンテナが利用するRDS(PostgreSQL)をプライベートサブネットへ作成
    const postgresRds = new rds.DatabaseInstance(this, 'postgres-container-security', {
      engine: rds.DatabaseInstanceEngine.postgres({version: rds.PostgresEngineVersion.VER_13_3}),
      vpc,
      vpcSubnets: {
        subnetType: ec2.SubnetType.PRIVATE
      }
    });
    
    // EKSクラスタからRDSへのデフォルトポートでの疎通を許可する
    postgresRds.connections.allowDefaultPortFrom(cluster);
    
    /**
     * kubernetes-external-secretsを使用してSecretManagerに格納されたRDSの接続情報を
     * KubernetesのSecretに連携する
     */ 
    
    // RDSの接続情報が格納されているSecret名
    const postgresRdsSecretInfo = postgresRds.secret;
    
    if(postgresRdsSecretInfo){
      // kubernetes-external-secretsのサービスアカウントに接続情報の読み取りを許可
      postgresRdsSecretInfo.grantRead(externalSecretServiceAccount);
      
      // kubernetes-external-secretsを使用して接続情報を連携するマニフェストの追加
      const postgresRdsSecretManifest = new eks.KubernetesManifest(this, 'rds-secret-manifest', {
        cluster,
        manifest:[{
          apiVersion: 'kubernetes-client.io/v1',
          kind: 'ExternalSecret',
          metadata: {
            name: 'postgres-rds-secret',
          },
          spec:{
            backendType: "secretsManager",
            data:[
              {
                key: postgresRdsSecretInfo.secretName,
                name: "dbcredential"
              }
            ]
          }
        }]
      });
      // このマニフェストがkubernetes-external-secretsのインストール後に実行されるよう依存関係を明示
      postgresRdsSecretManifest.node.addDependency(externalSecretHelm);
    }
  }
  
  /**
   * CDK Contextをパースして返す
   */ 
  private parseContext(): CdkContainerSecuritySampleContext{
    return {
      cidrPrefix: this.node.tryGetContext("CIDR_PREFIX") ?? DEFAULT_CONTEXT.cidrPrefix,
      desiredCapacity: this.node.tryGetContext("DESIRED_CAPACITY") ?? DEFAULT_CONTEXT.desiredCapacity,
      nodeInstanceType: this.node.tryGetContext("NODE_INSTANCE_TYPE") ?? DEFAULT_CONTEXT.nodeInstanceType,
      clusterName: this.node.tryGetContext("CLUSTER_NAME") ?? DEFAULT_CONTEXT.clusterName,
    }
  }
}

上記のようにソースコードを作成して、cdk deployコマンドでリソースをデプロイします。

$ yarn run cdk deploy

また、いくつかContextを定義しているので、パラメータの上書きが可能です。例えば、VPCのCIDRプレフィックスを10.2に変更するには、

$ yarn run cdk deploy -c CIDR_PREFIX=10.2

のようにして実行します。

AWS CDKによるセキュリティ向上ポイント

VPCセキュリテイ

VPC上にあるデータベースなどのリソースを保護するためには、ルートテーブルやセキュリティグループの設定・管理などの専門知識が必要です。例えば、本例で作成したRDSに関しては、

  1. 作成したEKSクラスタからはPostgreSQLのデフォルトポート(5432)でアクセスさせたい
  2. EKSクラスタからであっても5432以外からのアクセスは拒否したい
  3. EKSクラスタ以外からのアクセスはいかなるポートであっても拒否したい

という要件を満たす設定が必要です。

もちろん、きちんと勉強した専門家であれば手動でこのような環境を構築・管理することは可能ですが、専門家も人間である以上は人為ミスを想定せねばならず、その防止や発見のためにかなりのコストを費やすことになります。

一方で、AWS CDKにはHigh Level ConstructもしくはL2 Constructという概念があります。
上記のソース例を見ていただくと分かる通り、ソースコード上でセキュリティグループやルートテーブルの設定を明示的に記述していません。ただ1行だけ、

    // EKSクラスタからRDSへのデフォルトポートでの疎通を許可する
    postgresRds.connections.allowDefaultPortFrom(cluster);

と記述されているのみです。

実はAWS CDKでは、上に挙げた3つの要件がこの1行だけで実現できます
以下は、上記のソースによって作成されたセキュリティグループ(のうち1つ)のInbound ruleです。

スクリーンショット 2021-07-23 11.55.20.png

このようにAWS CDKではセキュリティグループやルートテーブルなどの設定を抽象化し、allowDefaultPortFromのような目的別に応じたメソッドを提供してくれます。このような工夫により、

  • 個別に手動設定することによる人為ミスの防止
  • 担当者の理解不足・知識不足による雑な設定(全ポートを解放してしまうなど)の防止
  • レビューの単純化(ルートテーブル/セキュリティグループの設定を1つ1つレビューするよりallowDefaultPortFromが使われていることを
    チェックする方がはるかに単純で楽。)

などを図ることが可能です。

認証情報の管理

AWS CDKやKubernetesなど、いわゆるInfrastructure as Codeがセキュリティに与えるメリットは多くありますが、一方でよくある失敗例は認証情報の流出かと思います。具体的には、
DBへの接続情報(ID/Pass)をソースコードや設定ファイルにハードコードしたままGitHubにpushして流出させてしまった
のような事態を防止しなければいけません。

ソース例で使用している@aws-cdk/aws-rdsDatabaseInstanceは、デフォルトで認証情報をAWS Secrets Managerに格納してくれます

スクリーンショット 2021-07-23 13.14.44.png

今回はこのようにAWS Secrets Managerに格納された認証情報をEKS上のコンテナから参照するため、EKSクラスタ上にKubernetes External Secretsというコントローラをインストールして、Secrets Manager上の情報をKubernetes上のSecretとして参照できるように設定します。

AWS CDKでは@aws-cdk/aws-eksライブラリを用いてクラスタへのHeml Chartのインストールやマニフェストの適用が可能なので、以下のようにインストールします。

    // Helm経由でkubernetes-external-secretsのインストール
    // @see https://github.com/external-secrets/kubernetes-external-secrets/tree/master/charts/kubernetes-external-secrets
    const externalSecretHelm = new eks.HelmChart(this,'external-secret', {
      cluster,
      repository: "https://external-secrets.github.io/kubernetes-external-secrets",
      chart: "kubernetes-external-secrets",
      values: {
        securityContext:{
          fsGroup: 65534
        },
        serviceAccount:{
          create:false,
          name: externalSecretServiceAccount.serviceAccountName
        },
        env:{
          AWS_REGION: this.region
        }
      }
    });

インストールしたコントローラを用いて、Secrets Manager上に格納された認証情報を連携します。

      // kubernetes-external-secretsのサービスアカウントに接続情報の読み取りを許可
      postgresRdsSecretInfo.grantRead(externalSecretServiceAccount);
      
      // kubernetes-external-secretsを使用して接続情報を連携するマニフェストの追加
      const postgresRdsSecretManifest = new eks.KubernetesManifest(this, 'rds-secret-manifest', {
        cluster,
        manifest:[{
          apiVersion: 'kubernetes-client.io/v1',
          kind: 'ExternalSecret',
          metadata: {
            name: 'postgres-rds-secret',
          },
          spec:{
            backendType: "secretsManager",
            data:[
              {
                key: postgresRdsSecretInfo.secretName,
                name: "dbcredential"
              }
            ]
          }
        }]
      });
      // このマニフェストがkubernetes-external-secretsのインストール後に実行されるよう依存関係を明示
      postgresRdsSecretManifest.node.addDependency(externalSecretHelm);

利用する側は以下の通り、Secretとしてマウントできます。

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: myapp
    volumeMounts:
    - name: rdsinfo
      mountPath: "/etc/secret"
      readOnly: true
  volumes:
  - name: rdsinfo
    secret:
      secretName: postgres-rds-secret
      items:
      - key: dbcredential
        path: rds-info

このように、これからEKSに乗るであろうコンテナの定義も含め、発行されたRDSの認証情報を一切ソースコードに残すことなくRDSへの接続を定義することが可能です。

policy-based deployment controllerをインストールする

では、公式ガイドラインに則り、作成したクラスタをContainer Security配下に登録します。

クラスタ定義の追加

ガイドラインの通り、Cloud OneのコンソールからContainer Securityを選択し、クラスタを追加します。

スクリーンショット 2021-07-23 13.51.49.png

すると以下のような指示が表示されます。

スクリーンショット 2021-07-23 13.53.29.png

ここではhelmコマンドを使ってクラスタにpolicy-based deployment controllerをインストールするよう指示されていますが、今回はここもAWS CDKで行います。

API KeyをAWS Secrets ManagerAWS Systems Managerのパラメータストアに登録する

上の画面ショットで黒塗りした箇所に書かれているAPI Keyは、先ほどのRDSの認証情報と同じく流出させてはいけないセキュリティ情報です。間違ってもソースコードや設定ファイルにハードコードしないよう、AWS Security Managerに登録しましょう

と、書きたいところだったのですが、実は数時間ハマった末、今回のユースケースではAWS Security Managerでは上手くいかないことが分かりました(後述)
なので、今回はAWS Systems Managerのパラメータストアに登録して、そこからCloudFormationのパラメータとして読み込む形にしたいと思います。

以下のように登録しました。

スクリーンショット 2021-07-23 20.38.14.png

policy-based deployment controllerをインストールするためのConstructを作成する

そのままStackに記述しても良かったのですが、少々ソースが長くなってきたのと、このコントローラのインストールは他のクラスタでも使い回すであろうことを鑑み、別のConstructに切り出しました。

基本的には、先にスクリーンショットを貼ったAdd Cluster完了時の画面に書いてあるhelm installのコマンドを@aws-cdk/aws-eksHelmChartで定義するよう書き換えただけです。

lib/helm-policybased-deployment-controller.ts
import * as cdk from '@aws-cdk/core';
import * as eks from '@aws-cdk/aws-eks';

/**
 * PolicyBasedDeploymentControllerのプロパティ
 */ 
export interface PolicyBasedDeploymentControllerProps{
    /** インストール対象のEKSクラスタ*/
    cluster: eks.ICluster,
    /** Cloud Oneが発行したAPI Key*/
    apiKey: cdk.SecretValue
}

/**
 * Trend Micro Cloud One Container SecurityのPolicy-based Deployment ControllerをインストールするHelmを適用するConstruct
 * @see https://cloudone.trendmicro.com/docs/container-security/cluster-add/#install-the-policy-based-deployment-controller
 */ 
export class PolicyBasedDeploymentController extends cdk.Construct{
    constructor(scope: cdk.Construct, id:string, props: PolicyBasedDeploymentControllerProps){
        super(scope, id);
        
        const {cluster, apiKey} = props;
        new eks.HelmChart(this, "policybased-deployment", {
            cluster,
            release: "trendmicro",
            namespace: "trendmicro-system",
            createNamespace: true,
            chart: "https://github.com/trendmicro/cloudone-container-security-helm/archive/master.tar.gz",
            values: {
                cloudOne: {
                    apiKey
                }
            } 
        })
    }
}

Stackにpolicy-based deployment controllerのインストールを追加する。

あとは、先ほど作成したlib/cdk-container-security-sample-stack.tsから呼び出すだけです。

長くなるので主な変更箇所のみ示します

lib/cdk-container-security-sample-stack.ts
import * as cdk from '@aws-cdk/core';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as eks from '@aws-cdk/aws-eks';
import * as rds from '@aws-cdk/aws-rds';

//追加
import {PolicyBasedDeploymentController} from './helm-policybased-deployment-controller';

//中略

export class CdkContainerSecuritySampleStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    //だいぶ中略
    
    //Cloud One Container Securityの導入
    new PolicyBasedDeploymentController(this, "cloudone", {
      cluster,
      apiKey: cdk.SecretValue.cfnParameter(new cdk.CfnParameter(this, 'CloudOneApiKey', {type: "AWS::SSM::Parameter::Value<String>", noEcho: true}))
    });
  }
  
  //中略
}

先ほども少し触れましたが、今回はCloudFormationのパラメータを使用して、Systems ManagerのパラメータストアからAPI Keyを参照することにしました。ということで、デプロイ時のコマンドが少し変わります。

$ yarn cdk deploy --parameters CloudOneApiKey=/cloudone/apikey/samplecluster

では、早速事項して結果を確認しましょう。

結果確認

cdk deployコマンドを打ってしばらく待つと、先ほど作成したEKS Clusterにtrendmicro-systemというnamespaceが作成され、その中にいくつかのPodが作成されます。

スクリーンショット 2021-07-23 20.30.20.png

全てのPodのステータスがRunningとなっているので正しく稼働していそうです。

また、CloudOneのコンソールを確認すると、

スクリーンショット 2021-07-23 20.32.14.png

このようにLast Evaluationの時間が更新され、クラスタが認識されたことが分かります。

まとめと今後について

ここまでで、

  • AWS CDKを用いてセキュアにAWS EKSクラスタとそこから参照するRDSを作成し
  • 同じくAWS CDKを用いて作成したEKSクラスタをCloud One Container Securityの監視対象にする

ことができました。
ガイドに記載の通り、ここからはCloud Oneのコンソールから「ポリシー」を定義して、クラスタに対して様々な監視やガードレールの敷設を行うことができます。VPCレベルでのガードレールの敷設はAWS Configなどからも可能ですが、Kubernetesのレイヤーに対してガードレールが設置できるのは非常にありがたいですね。

スクリーンショット 2021-07-23 21.42.27.png

本来、AWS ECR上のイメージに対してイメージスキャンを実行して、Container Securityのポリシーと連動して脆弱性のあるイメージのデプロイを防ぐためのDeep Security SmartCheckのセットアップについてAWS CDKで行う予定だったのですが、記事が長くなりそうなので一度ここで区切らせて頂こうかと思います。

また数日後にそちらのやり方についても公開できればと思います。

また、ソースの全体像についてはSmartCheckのセットアップを完了したのち、GitHubで公開しようと思います。

Appendix

EKS Clusterの片づけについて

今回作成したEKS Clusterおよびその周りのVPC,RDSを稼働させ続けているとそれなりの利用料が発生します。

Infrastructure as Codeのメリットの1つは、
(後で同じ環境が再現できるので)一度作った環境を躊躇なく消せる
ことです。

クラウド破産しないためにも、検証が終わったら

$ yarn run cdk destory 

で環境を削除しておくことを忘れないようにしましょう。

API Keyの管理にAWS SecretsManagerを使用するを断念した理由

本来はパラメータストアではなくSecretManagerにAPI Keyを登録して、

    //Cloud One Container Securityの導入
    //注意:失敗します
    new PolicyBasedDeploymentController(this, "cloudone", {
      cluster,
      apiKey: cdk.SecretValue.secretsManager('cloudone-secret',{jsonField:"SAMPLE_API_KEY"})
    });

のように呼び出した方がcdk deployのコマンドを変えることもなく、API Keyの管理自体も暗号化されていてスマートだと思ったのですが、うまく行きませんでした。

@aws-cdk/aws-eksHelmChartコンストラクトを使用すると、Cloud Formationレベルではカスタムリソースが作成されます。そして、cdk.SecretValue.secretsManagerを用いてSecretsManagerから値を参照すると、CloudFormationレベルでは動的参照が用いられます。

残念なことに、動的参照のリファレンスに記載の通り、

ssm-secure や secretsmanager などの安全な値の動的な参照は、現在カスタムリソースではサポートされていません。

ということで2021/07現在、HelmChartのプロパティに対してSecretManagerの値は解決できないようです。

今回は苦肉の策として、

  • ソースコードや設定ファイル上にAPI Keyをハードコードしなくて済む
  • cdk deployなどのコマンド実行時にAPI Keyそのものを入力しなくて済む

を最低限満たす方法として、CloudFormationパラメータ+パラメータストアという手段を採用しました。

14
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?