Test Yourself!

These ten questions turn on CloudFormation 24 behaviors that catch experienced developers: what Ref returns, escaping in Fn::Sub, secrets in outputs, conditional outputs, a failed first deploy, stale parameter values, the template size limit, named resources that cannot be replaced, locked exports and a rule that checks nothing. Every block ran on l1-localstack (LocalStack 4.13.1 63,725 , started as in Deploying the Network Stack, with the variables of awslocal), with cfn-lint 1.57.0 2,641 and cfn-guard 3.2.1 1,388 , in an empty ~/v5-ch7/ty folder. Where the emulator and AWS 24 disagree, the question asks what AWS does. Write down your answer, then check Appendix H: it gives LocalStack's real output, what AWS's documentation says, the reason and the section it comes from.

Setup

Setting up the quiz templates and ruleShell
mkdir -p ~/v5-ch7/ty && cd ~/v5-ch7/ty
cat > q.yaml <<'EOF'
Parameters:
  Stage: {Type: String, Default: dev}
  IdType: {Type: String, Default: S}
  ApiKey: {Type: String, NoEcho: true, Default: s3cr3t}
Resources:
  Queue: {Type: AWS::SQS::Queue}
  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: booknest-quiz-books
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions: [{AttributeName: id, AttributeType: !Ref IdType}]
      KeySchema: [{AttributeName: id, KeyType: HASH}]
Outputs:
  QueueRef: {Value: !Ref Queue}
  TableRef: {Value: !Ref Table}
  Secret: {Value: !Ref ApiKey}
  Stage: {Value: !Ref Stage, Export: {Name: quiz-stage}}
EOF
cat > s.yaml <<'EOF'
Resources:
  P:
    Type: AWS::SSM::Parameter
    Properties: {Type: String, Value: !Sub '${!AWS::Region} is ${AWS::Region}'}
EOF
cat > c.yaml <<'EOF'
Conditions: {InEurope: !Equals [!Ref 'AWS::Region', eu-west-1]}
Resources:
  Logs: {Type: AWS::S3::Bucket, Condition: InEurope}
  P: {Type: AWS::SSM::Parameter, Properties: {Type: String, Value: x}}
Outputs: {LogsBucket: {Value: !Ref Logs}}
EOF
printf '%s\n' 'Resources:' '  P: {Type: "AWS::SSM::Parameter", Properties:' \
  '    {Name: /quiz/imported, Type: String, Value: !ImportValue quiz-stage}}' > i.yaml
cat > v.guard <<'EOF'
let buckets = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule versioned { %buckets.Properties.VersioningConfiguration.Status == 'Enabled' }
EOF

Questions

Questions 1-4: Ref, Fn::Sub, NoEcho and conditionsYAML
cfn-lint s.yaml q.yaml c.yaml; echo "lint exit $?"    # for Questions 2-4
# 1. What does Ref return for each resource? (Section 7.4.1)
aws cloudformation deploy --stack-name booknest-quiz --template-file q.yaml >/dev/null
aws cloudformation describe-stacks --stack-name booknest-quiz --output text \
  --query "Stacks[0].Outputs[?ends_with(OutputKey,'Ref')].[OutputKey,OutputValue]"
# 2. What value does AWS store in the parameter? (Section 7.4.2)
aws cloudformation create-stack --stack-name booknest-sub --template-body file://s.yaml
# 3. Where is the NoEcho key masked, and where not? (Sections 7.3.2 and 7.13.1)
aws cloudformation describe-stacks --stack-name booknest-quiz --output text --query \
  "Stacks[0].[Parameters[?ParameterKey=='ApiKey'],Outputs[?OutputKey=='Secret']][]"
# 4. Logs exists only in eu-west-1. What does us-east-1 do? (Sections 7.3.4 and 7.13.1)
aws cloudformation deploy --stack-name booknest-cond --template-file c.yaml >/dev/null
aws cloudformation describe-stacks --stack-name booknest-cond --query 'Stacks[0].Outputs'
Questions 5-7: a failed create, parameter defaults and template sizeYAML
# 5. Fix the bucket name after the failed create, redeploy. What would AWS do? (Section 7.2.6)
echo 'Resources: {B: {Type: AWS::S3::Bucket, Properties: {BucketName: Book_Nest}}}' > r.yaml
aws cloudformation deploy --stack-name booknest-rb --template-file r.yaml >/dev/null 2>&1
aws cloudformation describe-stacks --stack-name booknest-rb --query 'Stacks[0].StackStatus'
sed -i 's/Book_Nest/booknest-rb-fixed/' r.yaml
aws cloudformation deploy --stack-name booknest-rb --template-file r.yaml 2>&1 | tail -1
# 6. Change Stage's default to prod and deploy. Which Stage does the stack use? (Section 7.6.2)
sed -i 's/Default: dev/Default: prod/' q.yaml
aws cloudformation deploy --stack-name booknest-quiz --template-file q.yaml >/dev/null
aws cloudformation list-exports --query 'Exports[?Name==`quiz-stage`].Value' --output text
# 7. Pad the template past 60 KB. Does it lint, and does it deploy? (Sections 7.6.2 and 7.6.4)
{ cat r.yaml; echo Metadata:; seq 1200 | awk '{printf "  N%d: %050d\n", $1, 0}'; } > big.yaml
wc -c < big.yaml && cfn-lint big.yaml && echo "lint ok"
aws cloudformation deploy --stack-name booknest-big --template-file big.yaml 2>&1 | grep -m1 ERROR
Questions 8-10: replacement, exports and a policy ruleYAML
# 8. Make the key id a number (N) instead of a string (S). What does AWS do? (Section 7.7.2)
aws cloudformation deploy --stack-name booknest-quiz --template-file q.yaml \
  --parameter-overrides IdType=N 2>&1 | tail -1
aws dynamodb describe-table --table-name booknest-quiz-books --query Table.AttributeDefinitions
# 9. Another stack imports quiz-stage. Now change the exported value. (Section 7.8.3)
aws cloudformation deploy --stack-name booknest-importer --template-file i.yaml >/dev/null
aws cloudformation deploy --stack-name booknest-quiz --template-file q.yaml \
  --parameter-overrides Stage=prod 2>&1 | tail -1
aws ssm get-parameter --name /quiz/imported --query Parameter.Value
# 10. The rule demands versioning on every bucket. Which templates pass? (Section 7.13.2)
for t in q.yaml r.yaml; do cfn-guard validate -r v.guard -d $t -S all | grep 'Status ='
  echo "exit ${PIPESTATUS[0]}"; done