Expressions
cfnpp contains a basic expression parser for evaluating conditions, mutating values, and more.
Expressions are composed of space separated values and operators, functions, etc are included using basic postfix notation.
(Why postfix? Because it's this is still largely a toy project and postfix is easy and fun to implement.)
That is, you push values onto the stack and then provide an operator to operate on the stack. That operator pops values off of the stack, and then pushes its result back on. Some basic examples of infix versus postfix:
12 == 10
--12 10 eq
"foo" != "bar"
--"foo" "bar" eq not
Valid tokens consist of:
- Numeric literals: any integer or float value in the format
1234
or1234.5678
- String literals: any value enclosed in double quotes (
"
); embedded quotes can be escaped with a backslash (\\
) - Variables and Parameters: any non-quoted string consisting of letters and numbers (no leading digit) is interpreted as a reference to a variable or parameter
- Operators: comparison/logical operators; currently supported:
- eq: equal
- not: inversion
- and: boolean and
- or: boolean or
Variables versus Parameter
Anything declared in the parameters
map under cfnpp
is interpreted as a reference to a parameter. Anything else is interpreted as a variable and uses the value available or causes an exception if it's not set.
Expressions Containing Parameters
cfnpp makes a best effort to simplify and solve any expression containing parameters as far as it can. Whatever remains it will attempt to convert into a CloudFormation condition to evaluate at the time of provisioning.
In some cases, cfnpp may be able to determine that the expression will always evaluate true or false regardless of the value of the referenced parameter in which case it will not create a condition.
For example, given:
cfnpp:
parameters:
LoadBalancerType: internal
variables:
CreateDnsRecords: true
Resources:
ExternalDnsRecord: !if
- LoadBalancerType "external" eq CreateDnsRecords and
-
Type: Route53::Record
Zone: 1234
Name: externalrecord
InternalDnsRecord: !if
- LoadBalancerType "internal" eq CreateDnsRecords or
-
Type: Route53::Record
Zone: 1234
Name: internalrecord
This will output:
Resources:
ExternalDnsRecord:
'Fn::If':
- Condition0
-
Type: 'Route53::Record'
Zone: 1234
Name: externalrecord
InternalDnsRecord:
Type: 'Route53::Record'
Zone: 1234
Name: internalrecord
Metadata:
Stack:
- ''
Conditions:
Condition0:
'Fn::Equals':
- external
-
Ref: LoadBalancerType
Note that the ExternalDnsRecords
has simplified away the CreateDnsRecords and has created a Condition to check the value of the LoadBalancerType parameter. The InternalDnsRecords
must be true since CreateDnsRecords is true so the value has simply been inserted.