AWS SAM template with YAML boolean environment variable

2024-01-14

Recently, I reviewed an AWS SAM template in which one of my team members added an environment variable ENABLE_FOO (name isn’t important) with the value true.

1
2
3
4
5
6
7
8
Resources:
  SomeVeryInterestingFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          ENABLE_FOO: true

Unquoted string are common in YAML, but this unquoted true isn’t a string, it’s a boolean, a subtle difference they had not considered.

This wasn’t a case I had encountered or even considered before and it left me quite skeptical, mostly because I didn’t know what to expect in the actual environment variable.

Said team member had tested true and false values, and confirmed they were passed as string "true" and "false" respectively.

Side note, props to them for testing the off state as well, please test your off state when doing soft rollout !

However, while true and false are the booleans in YAML 1.2, most YAML parsers will be somewhat compliant with YAML 1.1, which has 22 distinct values for boolean type.

What would happen in the future when another team member comes along and uses YES or On instead of true ?

So, that’s what I tested. I passed all those YAML 1.1 boolean values to a simple python function with returns them in a JSON object from the os.environ dict.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Resources:
  YamlBoolEnvVarCheckerFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: lambda_function.lambda_handler
      Runtime: python3.11
      MemorySize: 128
      Environment:
        Variables:
          BOOL_y: y
          BOOL_Y: Y
          BOOL_yes: yes
          BOOL_Yes: Yes
          BOOL_YES: YES
          BOOL_n: n
          BOOL_N: N
          BOOL_no: no
          BOOL_No: No
          BOOL_NO: NO
          BOOL_true: true
          BOOL_True: True
          BOOL_TRUE: TRUE
          BOOL_false: false
          BOOL_False: False
          BOOL_FALSE: FALSE
          BOOL_on: on
          BOOL_On: On
          BOOL_ON: ON
          BOOL_off: off
          BOOL_Off: Off
          BOOL_OFF: OFF
1
2
3
4
5
6
7
import json
import os

def lambda_handler(*_):
    return json.dumps(
        {k: v for k, v in os.environ.items() if k.startswith("BOOL")}
    )

Built it and ran it locally:

% sam build && sam local invoke | jq 'fromjson'

And this is the output. Most values give "true" and "false" as expected, the exceptions being the single letter booleans which give themselves as string value.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
  "BOOL_y": "y",
  "BOOL_Y": "Y",
  "BOOL_yes": "true",
  "BOOL_Yes": "true",
  "BOOL_YES": "true",
  "BOOL_n": "n",
  "BOOL_N": "N",
  "BOOL_no": "false",
  "BOOL_No": "false",
  "BOOL_NO": "false",
  "BOOL_true": "true",
  "BOOL_True": "true",
  "BOOL_TRUE": "true",
  "BOOL_false": "false",
  "BOOL_False": "false",
  "BOOL_FALSE": "false",
  "BOOL_on": "true",
  "BOOL_On": "true",
  "BOOL_ON": "true",
  "BOOL_off": "false",
  "BOOL_Off": "false",
  "BOOL_OFF": "false"
}

All in all, this boolean as environment variable seems like a footgun to me, so we improved our internal SAM template standards with a new rule.

Text environment variables in CF & SAM templates must be set as explicit strings with quotation marks.

Finally, having applied the new rule to the original change, the value of ENABLE_FOO became unmistakably clear.

1
2
3
4
5
6
7
8
Resources:
  SomeVeryInterestingFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          ENABLE_FOO: "true"
Actually, what we did was change the value to 1 which is also unambiguous and aligns with our best practices, but that was a good little exercise nonetheless.