TL;DR
DeletionProtection
タグを付けたリソースが削除できなくなるサービスコントロールポリシー(SCP)を作ります。Actionに書いたリソースの操作が対象になります。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DeletionProtectionTag",
"Effect": "Deny",
"Action": [
"logs:DeleteLogGroup",
"lambda:DeleteFunction"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/DeletionProtection": "true"
}
}
}
]
}
背景
エンジニアにとって最も恐ろしいのは、本番環境のリソースをうっかり削除してしまうことでしょう。
幸い、EC2インスタンスやRDSクラスターなど一部のリソースには「削除保護(終了保護)」の機能があります。
また、マネジメントコンソールで削除時にリソース名を入力させる画面が出る場合もあります。
問題は、全てのリソースがそうではないということです。
例えばCloudWatchロググループやLambda関数は比較的簡単に削除できてしまいます。
ほぼ全てのリソースにはタグが付けられるので、「削除保護タグ」みたいなものがあればなぁ...
作ってみた
そこで自作してみました。
使うのは AWS Organizations の サービスコントロールポリシー(SCP) です。
SCPを使うことで、Org内のAWSアカウントに対しアクセス許可を設定することができます。ポリシーの書き方はIAM等と同じなので、学習コストも低めです。
「削除保護タグ」は次のように実装できます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DeletionProtectionTag",
"Effect": "Deny",
"Action": [
"logs:DeleteLogGroup",
"lambda:DeleteFunction"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/DeletionProtection": "true"
}
}
}
]
}
上記はDeletionProtection
タグがついたリソースについて、記載したActionをDenyするポリシーです。記載していないActionは拒否されません。 今回は例として「CloudWatchロググループの削除」と「Lambda関数の削除」を拒否しています。
また、リソースからDeletionProtection
タグを外すことは拒否していません。今回はあくまで「うっかり削除」の防止が目的であり、本当にリソースを消したいときに面倒くさいからです。
CloudFormationテンプレート
本記事ではOrganizationやSCPの導入方法は割愛します。CloudFormationで作る場合は下記のようになります。
Resources:
SCPDeletionProtectionTag:
Type: AWS::Organizations::Policy
Properties:
Type: SERVICE_CONTROL_POLICY
Name: DeletionProtectionTag
TargetIds:
- # 下記をアタッチするOrganizational Unit
Content:
Version: 2012-10-17
Statement:
- Sid: DeletionProtectionTag
Effect: Deny
Action:
- logs:DeleteLogGroup
- lambda:DeleteFunction
# ここにActionを追加する
Resource: '*'
Condition:
StringEquals:
aws:ResourceTag/DeletionProtection: 'true'
削除保護タグの対応リソース
おおまかに、AWS services that work with IAM の「Services that work with IAM」の表でABACが✅Yesになっていれば、だいたい対応しています。ABACはattribute-based access controlの略です。
より正確には、Actions, resources, and condition keys for AWS services の子ページを確認し、Resource typesの表にaws:ResourceTag/${TagKey}
があれば対応しています。
また、もし対応していない場合は、ポリシーを作る画面で「このサービスにこのCondition Keyは使えないよ」と警告が出ます。
削除保護の挙動
作ったSCPの挙動を確認してみます。
AWS CLIの場合、エラーメッセージの末尾にservice control policy
があり、分かりやすかったです。
$ aws logs delete-log-group --log-group-name example-log-group
An error occurred (AccessDeniedException) when calling the DeleteLogGroup operation: User: arn:aws:iam::XXXXXXXXXXXX:user/tippy is not authorized to perform: logs:DeleteLogGroup on resource: arn:aws:logs:ap-northeast-1:XXXXXXXXXXXX:log-group:example-log-group:log-stream: with an explicit deny in a service control policy
一方マネジメントコンソールの場合、リソースにもよると思いますが、CloudWatchロググループでは特にエラーメッセージが出ませんでした。
実際に使ってみて
この仕組みを実際に弊社の全アカウントに導入しました。小並感ですが、感想は次のとおりです。
良かった点
- 導入がとても簡単
- 複数のAWSアカウントに対して、全ユーザーに対して、一括で設定できる
- マネジメントコンソールだけでなく、CLIやSDKによる操作にも有効
- SSOでAdminで入ったときにも有効
だめな点
- 一部リソースには対応していない
- Actionを1つ1つ追加するのが大変
- エラーメッセージが出ないことがある
制約は多いですが、ガードレールの1つとして十分使えると思います。
皆様の参考になれば幸いです。