Tampaknya ada banyak masalah dengan file template postingan blog tersebut. Anda tidak dapat menghapus tumpukan karena peran iam di tumpukan bertumpuk tidak memiliki izin yang cukup untuk menghapus semua sumber daya di tumpukan (lambda, peran iam, peristiwa, parameter ssm, dll).
Untuk memperbaiki kesalahan izin ini, Anda perlu membuat templat bersarang baru dengan izin tambahan untuk DeleteCFNLambdaExecutionRole
. Saya telah memberikan pembaruan dengan kebijakan terkelola arn:aws:iam::aws:policy/AdministratorAccess
, namun saya sangat menyarankan untuk mengetahui hak istimewa paling rendah untuk menghapus sumber daya Anda. Kebijakan yang saya tambahkan bukan praktik yang baik, namun karena saya tidak mengetahui kasus penggunaan Anda secara lengkap, itulah satu-satunya cara untuk menjamin bahwa kebijakan tersebut akan menghapus semuanya.
AWSTemplateFormatVersion: '2010-09-09'
Description: Schedule automatic deletion of CloudFormation stacks
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Input configuration
Parameters:
- StackName
- TTL
ParameterLabels:
StackName:
default: Stack name
TTL:
default: Time-to-live
Parameters:
StackName:
Type: String
Description: Stack name that will be deleted.
TTL:
Type: Number
Description: Time-to-live in minutes for the stack.
Resources:
DeleteCFNLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: ["lambda.amazonaws.com"]
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AdministratorAccess'
DeleteCFNLambda:
Type: "AWS::Lambda::Function"
DependsOn:
- DeleteCFNLambdaExecutionRole
Properties:
FunctionName: !Sub "DeleteCFNLambda-${StackName}"
Code:
ZipFile: |
import boto3
import os
import json
stack_name = os.environ['stackName']
def delete_cfn(stack_name):
try:
cfn = boto3.resource('cloudformation')
stack = cfn.Stack(stack_name)
stack.delete()
return "SUCCESS"
except:
return "ERROR"
def handler(event, context):
print("Received event:")
print(json.dumps(event))
return delete_cfn(stack_name)
Environment:
Variables:
stackName: !Ref 'StackName'
Handler: "index.handler"
Runtime: "python3.6"
Timeout: "5"
Role: !GetAtt DeleteCFNLambdaExecutionRole.Arn
DeleteStackEventRule:
DependsOn:
- DeleteCFNLambda
- GenerateCronExpression
Type: "AWS::Events::Rule"
Properties:
Description: Delete stack event
ScheduleExpression: !GetAtt GenerateCronExpression.cron_exp
State: "ENABLED"
Targets:
-
Arn: !GetAtt DeleteCFNLambda.Arn
Id: 'DeleteCFNLambda'
PermissionForDeleteCFNLambda:
Type: "AWS::Lambda::Permission"
Properties:
FunctionName: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:DeleteCFNLambda-${StackName}"
Action: "lambda:InvokeFunction"
Principal: "events.amazonaws.com"
SourceArn: !GetAtt DeleteStackEventRule.Arn
BasicLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: ["lambda.amazonaws.com"]
Action: "sts:AssumeRole"
Path: "/"
Policies:
- PolicyName: "lambda_policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*:*"
GenerateCronExpLambda:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
from datetime import datetime, timedelta
import os
import logging
import json
import cfnresponse
def deletion_time(ttl):
delete_at_time = datetime.now() + timedelta(minutes=int(ttl))
hh = delete_at_time.hour
mm = delete_at_time.minute
cron_exp = "cron({} {} * * ? *)".format(mm, hh)
return cron_exp
def handler(event, context):
print('Received event: %s' % json.dumps(event))
status = cfnresponse.SUCCESS
try:
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, status, {})
else:
ttl = event['ResourceProperties']['ttl']
responseData = {}
responseData['cron_exp'] = deletion_time(ttl)
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
except Exception as e:
logging.error('Exception: %s' % e, exc_info=True)
status = cfnresponse.FAILED
cfnresponse.send(event, context, status, {}, None)
Handler: "index.handler"
Runtime: "python3.6"
Timeout: "5"
Role: !GetAtt BasicLambdaExecutionRole.Arn
GenerateCronExpression:
Type: "Custom::GenerateCronExpression"
Version: "1.0"
Properties:
ServiceToken: !GetAtt GenerateCronExpLambda.Arn
ttl: !Ref 'TTL'
Setelah Anda melakukan perubahan ini, Anda perlu mengunggah ke s3 dan memperbarui referensi di tumpukan utama ke versi templat Anda.
AWSTemplateFormatVersion: '2010-09-09'
Description: Demo stack, creates one SSM parameter and gets deleted after 5 minutes.
Resources:
DemoParameter:
Type: "AWS::SSM::Parameter"
Properties:
Type: "String"
Value: "date"
Description: "SSM Parameter for running date command."
AllowedPattern: "^[a-zA-Z]{1,10}$"
DependsOn: DeleteAfterTTLStack
DeleteAfterTTLStack:
Type: "AWS::CloudFormation::Stack"
Properties:
TemplateURL: 'https://your-bucket.s3.amazonaws.com/delete_resources.yaml'
Parameters:
StackName: !Ref 'AWS::StackName'
TTL: '5'
Anda mungkin perlu menambahkan bidang DependsOn: DeleteAfterTTLStack
ke setiap sumber daya untuk memastikan izin tidak dihapus sebelum semua sumber daya dihapus, jika tidak, kesalahan izin dapat terjadi.
Meskipun ini seharusnya berhasil, saya setuju dengan @John Rotenstein bahwa cloudformation mungkin bukan solusi terbaik. Pertama, mengelola izin bisa menjadi masalah besar. Sangat mudah untuk memberikan izin terlalu banyak atau terlalu sedikit saat mengonfigurasi templat ini.
person
Ben Bloom
schedule
14.01.2020