amplify deleteコマンドによるアプリケーションの削除を禁止する方法

2022.07.21

こんにちは、最近やらかしエピソードが多い気がします。八木です。

先月、新卒のチーム開発研修でAmplifyを用いて開発を行っていました。
私は検証のために作成したAmplifyアプリケーション内の1つの環境を削除しようと、Amplify CLIコマンドamplify deleteを実行しました。

するとどうなったでしょうか?

Amplify アプリケーションが削除されました。

Amplifyに疎い私は間違えたコマンドを実行してしまいました。特定のアプリケーション内の特定の環境を削除するのはamplify env removeコマンドです。

amplify deleteコマンドは全ての環境を含む、Amplifyアプリケーション自体を削除します。

実際にamplify deleteコマンドを実行した際も、しっかりと警告を出してくれていました。

$ amplify delete
? Are you sure you want to continue? This CANNOT be undone. (This will delete all the environments of the project from the cloud and wipe out all the local files created by Amplify CLI) (y/N)

あー、削除前の確認だよね!問題なし!と警告メッセージを無視し、Yesと答えた私は数秒後、絶叫しました。

以上の失敗を踏まえ、この記事ではamplify deleteコマンドによるアプリケーションの削除を防ぐ方法を紹介します。

amplify deleteコマンドを防ぐ方法

上記コマンドによるアプリケーションの削除を防ぐ方法は、以下の2種類があります。 1. Amplify CLIのCommand Hooksを使用する 2. IAM権限で制限する

順番に見ていきます。

1. Amplify CLIのCommand Hooksを使用する

Amplify CLIにはCommand Hooksという機能が存在します。これはGit Hooksのような機能で、Amplify CLIの各コマンドの前後、実行途中にカスタムスクリプトを実行する機能です。
この機能を使い、amplify deleteコマンドのbeforeアクションに、必ず失敗するスクリプトを設定することで、コマンドを制限することができます。

Command Hooksには任意の言語を使用できますが、今回はシェルスクリプトで実装します。 プロジェクトにamplify/hooksディレクトリを作成し、その中にpre-delete.shファイルを作成します。

amplify/hooks/pre-delete.sh

echo "amplify delete command is prohibited."
exit 1

これでamplify deleteコマンドを実行した際、Command Hooksが起動し、コマンドを失敗させることができました。

$ amplify delete

----- 🪝 pre-delete execution start -----
amplify delete command is prohibited.

🛑 pre-delete hook script exited with exit code 1

🛑 exiting Amplify process...

2. IAM権限で制限する

1の方法でコマンドの実行制限はできましたが、スクリプトファイルを編集することで、簡単にコマンドを実行できるようになってしまします。背景を知らないメンバーがファイルを編集して実行してしまうなどの可能性は残ります。そこで必要になってくるのが、IAM権限の制限による実行制限です。

しかし結論から言うと、IAM権限を用いてamplify deleteコマンドのみを制限することはできません。これはamplify deleteコマンドが依存しているIAM権限による問題です。ここでは可能な限り、リソースを維持する方法についてご紹介します。

まず、Amplify CLIが全てのアクションを実行するために必要なIAM権限は、こちらのドキュメントに乗っています。このうち、amplify deleteコマンドに関連しそうなものはamplify:DeleteAppです。

しかし、このamplify:DeleteAppを拒否しても、Amplifyのリソースは削除されてしまいます。
Amplifyは1つのアプリケーションの中に複数の環境を持ちます。環境はそれぞれCloudformationで管理されており、APIやStorageなどはネストしたスタックに定義されます。

amplify deleteコマンドはアプリケーション中の各環境、つまり各Cloudformationスタックを削除した後、アプリケーションの削除を行います。このため、amplify:DeleteAppを拒否した状態でamplify deleteを行うと、全てのリソース(Cloudformationスタック)が削除され、空のアプリケーションが残る状態になってしまいます。

一方でこのような仕組みのため、cloudformation:DeleteStackを拒否することで、アプリケーションのリソースの削除を防ぐことができます。
しかし注意したいのが、上記アクションはamplify deleteコマンドを防ぐアクションではなく、Cloudformationのスタック削除を許可するアクションという点です。Amplifyの各環境に対してスタックは作成されるため、検証環境をのみを削除するといった操作までも実行できなくなってしまいます。また、Amplifyとは関係ないリソースにまで影響が及んでしまいます。

このため、IAMでcloudformation:DeleteStackを拒否する際には、タグで制限をかけます。
Amplifyは環境を作成した際、デフォルトではuser:Stackタグに環境名、user:Applicationタグにアプリケーション名が入ります。このタグを使って、削除保護を行う環境を絞り込むことができます。
なお、これらのスタックに付与されるタグは変更することも可能です。1

以下のようなIAMポリシーをコマンド実行ユーザにアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": [
                "cloudformation:DeleteStack"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/user:Application": "amplifyapp",
                    "aws:ResourceTag/user:Stack": [
                        "stag",
                        "prod"
                    ]
                }
            }
        }
    ]
}

これで、Amplifyアプリケーション(amplifyapp)の環境(stag, prod)は削除ができなくなりました。

この状態でamplify deleteを実行すると、権限がないため失敗します。
以下はIAMによる制限がある状態の環境(prod)と制限がない状態の環境(dev)をもつアプリケーションを削除したときのログです。

$ amplify delete
? Are you sure you want to continue? This CANNOT be undone. (This will delete all the environments of the project from the cloud and wi
pe out all the local files created by Amplify CLI) Yes
⠋ Deleting resources from the cloud. This may take a few minutes...
Deleting env: prod.

Deleting env: dev.
⠹ Deleting resources from the cloud. This may take a few minutes...Error deleting stack amplify-amplifyapp-prod-02110

Error occurred while deleting env: prod.
User: arn:aws:iam::123456789012:user/amplify-admin is not authorized to perform: cloudformation:DeleteStack on resource: arn:aws:cloudformation:ap-northeast-1:123456789012:stack/amplify-amplifyapp-prod-02110/960dc4c0-083f-11ed-afed-0eec3649f2d5 with an explicit deny in an identity-based policy
✖ Project delete failed.

prod環境の削除に失敗し、結果としてdev環境のリソースのみ削除されました。
しかしここで注意したいのが、コマンドは綺麗な状態で終了しないと言う点です。私の実行したアプリケーション内では、まずcli.jsonファイルが削除されました。
また、dev環境内のリソースは削除されたものの、dev環境自体は残ってしまいました。

$ aws amplify list-backend-environments --app-id d2v6kz0b15ue8p
backendEnvironments:
- backendEnvironmentArn: arn:aws:amplify:ap-northeast-1:123456789012:apps/d2v6kz0b15ue8p/backendenvironments/dev
  createTime: '2022-07-21T00:30:03.111000+09:00'
  deploymentArtifacts: amplify-amplifyapp-dev-03002-deployment
  environmentName: dev
  stackName: amplify-amplifyapp-dev-03002
  updateTime: '2022-07-21T00:30:03.111000+09:00'
- backendEnvironmentArn: arn:aws:amplify:ap-northeast-1:123456789012:apps/d2v6kz0b15ue8p/backendenvironments/prod
  createTime: '2022-07-21T00:21:10.867000+09:00'
  deploymentArtifacts: amplify-amplifyapp-prod-02110-deployment
  environmentName: prod
  stackName: amplify-amplifyapp-prod-02110
  updateTime: '2022-07-21T00:21:10.867000+09:00'

加えて、Amplify CLIの実装が変わった場合、動作が変わる可能性は十分にあります。Amplify CLIの仕様で動作が定められているわけではないため、注意してください。

以上のような理由から、IAM権限による絞り込みは、あくまでリソース保持の最終ラインを目的とし、amplify deleteコマンドの防止は1の方法をメインで用いると良いでしょう。

まとめ

amplify deleteコマンドはアプリケーション自体を削除します。
IAM権限を制限することで、アプリケーションのリソース削除を防ぐことはできますが、綺麗な状態で残すことができません。
防ぐためには、まずCommand Hooksを使用してコマンドの実行を制限し、加えて、ガードレールとして、IAM権限を制限すると良いでしょう。