ดูเหมือนว่าจะมีปัญหามากมายกับไฟล์เทมเพลตของโพสต์บนบล็อก คุณไม่สามารถลบสแต็กได้เนื่องจากบทบาท iam ในสแต็กที่ซ้อนกันไม่มีสิทธิ์เพียงพอที่จะลบทรัพยากรทั้งหมดในสแต็ก (lambda, บทบาท iam, เหตุการณ์, พารามิเตอร์ ssm ฯลฯ)
หากต้องการแก้ไขข้อผิดพลาดนี้ด้วยสิทธิ์ คุณจะต้องสร้างเทมเพลตที่ซ้อนกันใหม่พร้อมสิทธิ์เพิ่มเติมสำหรับ DeleteCFNLambdaExecutionRole
ของคุณ ฉันได้ให้การอัปเดตด้วยนโยบายที่มีการจัดการ arn:aws:iam::aws:policy/AdministratorAccess
แต่ฉันขอแนะนำเป็นอย่างยิ่ง ให้ค้นหาสิทธิ์ขั้นต่ำในการลบทรัพยากรของคุณ นโยบายที่ฉันเพิ่มนั้น ไม่ใช่ แนวทางปฏิบัติที่ดี แต่เนื่องจากฉันไม่ทราบกรณีการใช้งานทั้งหมดของคุณ จึงเป็นวิธีเดียวที่จะรับประกันว่าจะลบทุกอย่าง
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'
เมื่อคุณทำการเปลี่ยนแปลงนี้แล้ว คุณจะต้องอัปโหลดไปยัง s3 และอัปเดตข้อมูลอ้างอิงในสแต็กหลักเป็นเทมเพลตเวอร์ชันของคุณ
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'
คุณอาจต้องเพิ่มฟิลด์ DependsOn: DeleteAfterTTLStack
ลงในแต่ละทรัพยากรเพื่อให้แน่ใจว่าสิทธิ์จะไม่ถูกลบก่อนที่ทรัพยากรทั้งหมดจะถูกลบออก มิฉะนั้นอาจเกิดข้อผิดพลาดในการอนุญาตได้
แม้ว่าสิ่งนี้ควรจะได้ผล แต่ฉันเห็นด้วยกับ @John Rotenstein ว่าการสร้างคลาวด์อาจไม่ใช่ทางออกที่ดีที่สุด ประการแรก การจัดการสิทธิ์อาจเป็นปัญหาใหญ่ เป็นเรื่องง่ายที่จะให้สิทธิ์มากเกินไปหรือน้อยเกินไปเมื่อกำหนดค่าเทมเพลตนี้
person
Ben Bloom
schedule
14.01.2020