Skip to content

Commit ddfd3df

Browse files
committed
Manage bucket via CLI scripts, not CloudFormation
- setup-bucket.sh creates bucket via CLI, registers name in a CFN stack - cleanup-bucket.sh empties and deletes bucket, then deletes the stack - prereq-bucket.yaml is now a thin parameter-based template (no S3 resource) - test-cfn.py reverted to clean prereq detection (no inline fallback) - All 10 templates now pass deploy+delete including 003-s3
1 parent bc8002d commit ddfd3df

4 files changed

Lines changed: 104 additions & 18 deletions

File tree

cfn/cleanup-bucket.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/bin/bash
2+
# Empty and delete the shared tutorial S3 bucket, then delete the CloudFormation stack.
3+
# Usage: ./cfn/cleanup-bucket.sh
4+
set -eo pipefail
5+
6+
STACK_NAME="tutorial-prereqs-bucket"
7+
8+
BUCKET_NAME=$(aws cloudformation describe-stacks --stack-name "$STACK_NAME" \
9+
--query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' --output text 2>/dev/null)
10+
11+
if [ -z "$BUCKET_NAME" ] || [ "$BUCKET_NAME" = "None" ]; then
12+
echo "No bucket stack found."
13+
exit 0
14+
fi
15+
16+
echo "Bucket: $BUCKET_NAME"
17+
18+
# Empty the bucket (including versions)
19+
OBJ_COUNT=$(aws s3api list-objects-v2 --bucket "$BUCKET_NAME" --query 'KeyCount' --output text 2>/dev/null || echo "0")
20+
if [ "$OBJ_COUNT" -gt 0 ] 2>/dev/null; then
21+
echo "Emptying $OBJ_COUNT objects..."
22+
aws s3 rm "s3://$BUCKET_NAME" --recursive --quiet
23+
fi
24+
25+
# Delete versions and delete markers
26+
aws s3api list-object-versions --bucket "$BUCKET_NAME" \
27+
--query '{Objects: Versions[].{Key:Key,VersionId:VersionId}, Quiet: true}' \
28+
--output json 2>/dev/null | \
29+
aws s3api delete-objects --bucket "$BUCKET_NAME" --delete file:///dev/stdin > /dev/null 2>&1 || true
30+
31+
aws s3api list-object-versions --bucket "$BUCKET_NAME" \
32+
--query '{Objects: DeleteMarkers[].{Key:Key,VersionId:VersionId}, Quiet: true}' \
33+
--output json 2>/dev/null | \
34+
aws s3api delete-objects --bucket "$BUCKET_NAME" --delete file:///dev/stdin > /dev/null 2>&1 || true
35+
36+
echo "Deleting bucket: $BUCKET_NAME"
37+
aws s3api delete-bucket --bucket "$BUCKET_NAME"
38+
39+
echo "Deleting stack: $STACK_NAME"
40+
aws cloudformation delete-stack --stack-name "$STACK_NAME"
41+
aws cloudformation wait stack-delete-complete --stack-name "$STACK_NAME"
42+
43+
echo "Done."

cfn/prereq-bucket.yaml

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
11
AWSTemplateFormatVersion: '2010-09-09'
22
Description: >-
3-
Shared S3 bucket for tutorial artifacts. Deploy once, reference from other
4-
tutorial stacks via cross-stack export. Empty the bucket before deleting.
3+
Shared S3 bucket reference for tutorials. The bucket is created and deleted
4+
by setup-bucket.sh and cleanup-bucket.sh. This stack just exports the name
5+
so other tutorial stacks can import it.
6+
7+
Parameters:
8+
BucketName:
9+
Type: String
10+
Description: Name of the bucket created by setup-bucket.sh
511

612
Resources:
7-
TutorialBucket:
8-
Type: AWS::S3::Bucket
9-
DeletionPolicy: Delete
10-
Properties:
11-
BucketEncryption:
12-
ServerSideEncryptionConfiguration:
13-
- ServerSideEncryptionRule:
14-
ServerSideEncryptionByDefault:
15-
SSEAlgorithm: AES256
16-
Tags:
17-
- Key: tutorial
18-
Value: shared-bucket
13+
Placeholder:
14+
Type: AWS::CloudFormation::WaitConditionHandle
1915

2016
Outputs:
2117
BucketName:
2218
Description: Name of the shared tutorial bucket
23-
Value: !Ref TutorialBucket
19+
Value: !Ref BucketName
2420
Export:
2521
Name: !Sub '${AWS::StackName}-BucketName'
2622
BucketArn:
2723
Description: ARN of the shared tutorial bucket
28-
Value: !GetAtt TutorialBucket.Arn
24+
Value: !Sub 'arn:aws:s3:::${BucketName}'
2925
Export:
3026
Name: !Sub '${AWS::StackName}-BucketArn'

cfn/setup-bucket.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/bin/bash
2+
# Create the shared tutorial S3 bucket and register it with CloudFormation.
3+
# Usage: ./cfn/setup-bucket.sh
4+
set -eo pipefail
5+
6+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7+
STACK_NAME="tutorial-prereqs-bucket"
8+
ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)
9+
REGION=$(aws configure get region 2>/dev/null || echo "us-east-1")
10+
BUCKET_NAME="tutorial-bucket-${ACCOUNT_ID}-${REGION}"
11+
12+
# Check if stack already exists
13+
STATUS=$(aws cloudformation describe-stacks --stack-name "$STACK_NAME" \
14+
--query 'Stacks[0].StackStatus' --output text 2>/dev/null || echo "NONE")
15+
16+
if [ "$STATUS" = "CREATE_COMPLETE" ] || [ "$STATUS" = "UPDATE_COMPLETE" ]; then
17+
EXISTING=$(aws cloudformation describe-stacks --stack-name "$STACK_NAME" \
18+
--query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' --output text)
19+
echo "Bucket already exists: $EXISTING"
20+
exit 0
21+
fi
22+
23+
echo "Creating bucket: $BUCKET_NAME"
24+
if [ "$REGION" = "us-east-1" ]; then
25+
aws s3api create-bucket --bucket "$BUCKET_NAME"
26+
else
27+
aws s3api create-bucket --bucket "$BUCKET_NAME" \
28+
--create-bucket-configuration LocationConstraint="$REGION"
29+
fi
30+
31+
aws s3api put-bucket-encryption --bucket "$BUCKET_NAME" \
32+
--server-side-encryption-configuration \
33+
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
34+
35+
aws s3api put-public-access-block --bucket "$BUCKET_NAME" \
36+
--public-access-block-configuration \
37+
'BlockPublicAcls=true,BlockPublicPolicy=true,IgnorePublicAcls=true,RestrictPublicBuckets=true'
38+
39+
echo "Registering bucket with CloudFormation stack: $STACK_NAME"
40+
aws cloudformation deploy \
41+
--template-file "$SCRIPT_DIR/prereq-bucket.yaml" \
42+
--stack-name "$STACK_NAME" \
43+
--parameter-overrides "BucketName=$BUCKET_NAME"
44+
45+
echo "Done. Bucket: $BUCKET_NAME"
46+
echo "Other stacks can import: !ImportValue ${STACK_NAME}-BucketName"

test-cfn.py

100644100755
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python3
1+
#!/usr/bin/env python
22
"""Test all CloudFormation templates: validate, deploy, verify, delete.
33
44
Usage: python3 test-cfn.py [--parallel N] [--skip-deploy] [--region REGION]
@@ -138,7 +138,8 @@ def ensure_prereqs(cfn, repo_root, needed_stacks):
138138
waiter.wait(StackName=stack_name, WaiterConfig={"Delay": 15, "MaxAttempts": 40})
139139
print(f" Prereq {stack_name}: ready")
140140
except Exception as e:
141-
print(f" Prereq {stack_name}: FAILED ({e})")
141+
print(f" Prereq {stack_name}: CFN deploy failed ({e})")
142+
print(f" Prereq {stack_name}: Run ./cfn/setup-bucket.sh first, then retry.")
142143
failed.add(stack_name)
143144
try:
144145
cfn.delete_stack(StackName=stack_name)

0 commit comments

Comments
 (0)