@znemz/cfn-include 2.1.7 → 2.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -0
- package/index.js +60 -0
- package/lib/schema.js +3 -0
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -44,6 +44,8 @@ For example, [`Fn::Include`](#fninclude) provides a convenient way to include fi
|
|
|
44
44
|
- [Fn::Eval](#fneval)
|
|
45
45
|
- [Fn::IfEval](#fnifeval)
|
|
46
46
|
- [Fn::JoinNow](#fnjoinnow)
|
|
47
|
+
- [Fn::SubNow](#fsubnow)
|
|
48
|
+
- [Fn::RefNow](#frefnow)
|
|
47
49
|
- [Fn::ApplyTags](#fnapplytags)
|
|
48
50
|
- [Fn::Outputs](#fnoutputs)
|
|
49
51
|
- [More Examples](#more-examples)
|
|
@@ -1071,6 +1073,134 @@ Fn::JoinNow:
|
|
|
1071
1073
|
arn:aws:s3:::c1-acme-iam-cache-engine-${AWS::AccountId}-us-east-1$CFT_STACK_SUFFIX
|
|
1072
1074
|
```
|
|
1073
1075
|
|
|
1076
|
+
## Fn::SubNow
|
|
1077
|
+
|
|
1078
|
+
`Fn::SubNow` performs immediate string substitution similar to AWS CloudFormation's `Fn::Sub`, but evaluates during template preprocessing rather than at stack creation time. It supports variable substitution using `${VariableName}` syntax and AWS pseudo-parameters.
|
|
1079
|
+
|
|
1080
|
+
The function supports two input formats:
|
|
1081
|
+
|
|
1082
|
+
**String format:**
|
|
1083
|
+
```yaml
|
|
1084
|
+
Fn::SubNow: "arn:aws:s3:::bucket-${BucketSuffix}"
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
**Array format with variables:**
|
|
1088
|
+
```yaml
|
|
1089
|
+
Fn::SubNow:
|
|
1090
|
+
- "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroupName}"
|
|
1091
|
+
- LogGroupName: /aws/lambda/my-function
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
**Supported AWS pseudo-parameters:**
|
|
1095
|
+
- `${AWS::AccountId}` - AWS Account ID
|
|
1096
|
+
- `${AWS::Region}` - AWS Region
|
|
1097
|
+
- `${AWS::StackName}` - Stack name
|
|
1098
|
+
- `${AWS::StackId}` - Stack ID
|
|
1099
|
+
- `${AWS::Partition}` - AWS Partition (e.g., 'aws')
|
|
1100
|
+
- `${AWS::URLSuffix}` - URL suffix (e.g., 'amazonaws.com')
|
|
1101
|
+
|
|
1102
|
+
Variables can be provided via:
|
|
1103
|
+
1. The `inject` option passed to `cfn-include`
|
|
1104
|
+
2. Explicit variables in the array format (takes precedence over `inject`)
|
|
1105
|
+
3. Environment variables when using the `doEnv` option
|
|
1106
|
+
|
|
1107
|
+
**Example with environment variables:**
|
|
1108
|
+
```yaml
|
|
1109
|
+
BucketName: !SubNow "my-bucket-${Environment}-${AWS::Region}"
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
With `CFN_INCLUDE_DO_ENV=true` and environment variable `ENVIRONMENT=prod`, this resolves to:
|
|
1113
|
+
```yaml
|
|
1114
|
+
BucketName: "my-bucket-prod-us-east-1"
|
|
1115
|
+
```
|
|
1116
|
+
|
|
1117
|
+
## Fn::RefNow
|
|
1118
|
+
|
|
1119
|
+
`Fn::RefNow` resolves a reference immediately during template preprocessing, similar to AWS CloudFormation's `Fn::Ref` but evaluated at processing time rather than stack creation time. It resolves references to parameters, variables, and AWS pseudo-parameters.
|
|
1120
|
+
|
|
1121
|
+
**Basic syntax:**
|
|
1122
|
+
```yaml
|
|
1123
|
+
BucketRef:
|
|
1124
|
+
Fn::RefNow: BucketName
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
or using YAML tag syntax:
|
|
1128
|
+
```yaml
|
|
1129
|
+
BucketRef: !RefNow BucketName
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
**Supported AWS pseudo-parameters:**
|
|
1133
|
+
- `AWS::AccountId` - AWS Account ID (environment: `AWS_ACCOUNT_ID` or `AWS_ACCOUNT_NUM`, fallback: `${AWS::AccountId}`)
|
|
1134
|
+
- `AWS::Region` - AWS Region (environment: `AWS_REGION`, fallback: `${AWS::Region}`)
|
|
1135
|
+
- `AWS::StackName` - Stack name (environment: `AWS_STACK_NAME`, fallback: `${AWS::StackName}`)
|
|
1136
|
+
- `AWS::StackId` - Stack ID (environment: `AWS_STACK_ID`, fallback: `${AWS::StackId}`)
|
|
1137
|
+
- `AWS::Partition` - AWS Partition (environment: `AWS_PARTITION`, default: `'aws'`)
|
|
1138
|
+
- `AWS::URLSuffix` - URL suffix (environment: `AWS_URL_SUFFIX`, default: `'amazonaws.com'`)
|
|
1139
|
+
- `AWS::NotificationARNs` - SNS topic ARNs for notifications (environment: `AWS_NOTIFICATION_ARNS`, fallback: `${AWS::NotificationARNs}`)
|
|
1140
|
+
|
|
1141
|
+
**Reference resolution priority:**
|
|
1142
|
+
1. AWS pseudo-parameters (with environment variable fallbacks)
|
|
1143
|
+
2. Variables from the `inject` option
|
|
1144
|
+
3. Variables from the current scope (useful with `Fn::Map`)
|
|
1145
|
+
|
|
1146
|
+
**Reference indirection:**
|
|
1147
|
+
If a resolved reference is a string, it will be treated as a reference name and resolved again. This enables reference chaining, useful when using `Fn::RefNow` with `Fn::Map`:
|
|
1148
|
+
|
|
1149
|
+
```yaml
|
|
1150
|
+
Fn::Map:
|
|
1151
|
+
- [BucketVar1, BucketVar2]
|
|
1152
|
+
- BucketName:
|
|
1153
|
+
Fn::RefNow: _
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
With `inject: { BucketVar1: "my-bucket-1", BucketVar2: "my-bucket-2" }`, this via
|
|
1157
|
+
|
|
1158
|
+
`$ Bucket1=my-bucket-1 BucketVar2=my-bucket-2 cnf-include examples/refNow.yml --enable env,eval` via exports / env
|
|
1159
|
+
|
|
1160
|
+
or
|
|
1161
|
+
|
|
1162
|
+
`$ cnf-include examples/refNow.yml --enable env,eval --inject '{"BucketVar1":"my-bucket-1","BucketVar2":"my-bucket-2"}'` via inject
|
|
1163
|
+
|
|
1164
|
+
resolves to:
|
|
1165
|
+
```yaml
|
|
1166
|
+
BucketVar1: my-bucket-1
|
|
1167
|
+
BucketVar2: my-bucket-2
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
The `_` placeholder resolves to `"BucketVar1"` or `"BucketVar2"`, which are then resolved again to their actual values.
|
|
1171
|
+
|
|
1172
|
+
**Example with injected variables:**
|
|
1173
|
+
```yaml
|
|
1174
|
+
Resources:
|
|
1175
|
+
Bucket:
|
|
1176
|
+
Type: AWS::S3::Bucket
|
|
1177
|
+
Properties:
|
|
1178
|
+
BucketName:
|
|
1179
|
+
Fn::RefNow: BucketName
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
With `inject: { BucketName: "my-app-bucket" }`, this resolves to:
|
|
1183
|
+
```yaml
|
|
1184
|
+
BucketName: "my-app-bucket"
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
**Example with AWS pseudo-parameters:**
|
|
1188
|
+
```yaml
|
|
1189
|
+
LogGroup:
|
|
1190
|
+
Fn::RefNow: AWS::StackName
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
With `AWS_STACK_NAME=my-stack`, this resolves to:
|
|
1194
|
+
```yaml
|
|
1195
|
+
LogGroup: "my-stack"
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
**Unresolved AWS pseudo-parameters:**
|
|
1199
|
+
If an AWS pseudo-parameter is not set via environment variables, it falls back to a placeholder string (e.g., `${AWS::AccountId}`). Only `AWS::Partition` and `AWS::URLSuffix` have hardcoded defaults since they are rarely environment-specific.
|
|
1200
|
+
|
|
1201
|
+
**Error handling:**
|
|
1202
|
+
If a reference cannot be resolved, `Fn::RefNow` will throw an error. Ensure all referenced parameters and variables are available via inject, scope, or environment variables.
|
|
1203
|
+
|
|
1074
1204
|
## Fn::ApplyTags
|
|
1075
1205
|
|
|
1076
1206
|
See [ApplyTags test file](t/tests/applyTags.yml).
|
package/index.js
CHANGED
|
@@ -90,6 +90,18 @@ module.exports = async function (options) {
|
|
|
90
90
|
* is subtituted with Value
|
|
91
91
|
* @param {boolean} opts.doLog log all arguments at the include recurse level
|
|
92
92
|
*/
|
|
93
|
+
function getAwsPseudoParameters() {
|
|
94
|
+
return {
|
|
95
|
+
'AWS::AccountId': process.env.AWS_ACCOUNT_ID || process.env.AWS_ACCOUNT_NUM || '${AWS::AccountId}',
|
|
96
|
+
'AWS::Partition': process.env.AWS_PARTITION || 'aws',
|
|
97
|
+
'AWS::Region': process.env.AWS_REGION || '${AWS::Region}',
|
|
98
|
+
'AWS::StackId': process.env.AWS_STACK_ID || '${AWS::StackId}',
|
|
99
|
+
'AWS::StackName': process.env.AWS_STACK_NAME || '${AWS::StackName}',
|
|
100
|
+
'AWS::URLSuffix': process.env.AWS_URL_SUFFIX || 'amazonaws.com',
|
|
101
|
+
'AWS::NotificationARNs': process.env.AWS_NOTIFICATION_ARNS || '${AWS::NotificationARNs}',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
async function recurse({ base, scope, cft, ...opts }) {
|
|
94
106
|
if (opts.doLog) {
|
|
95
107
|
|
|
@@ -441,6 +453,54 @@ async function recurse({ base, scope, cft, ...opts }) {
|
|
|
441
453
|
return toJoinArray.join(delimitter);
|
|
442
454
|
});
|
|
443
455
|
}
|
|
456
|
+
if (cft['Fn::SubNow']) {
|
|
457
|
+
return recurse({ base, scope, cft: cft['Fn::SubNow'], ...opts }).then((input) => {
|
|
458
|
+
let template = input;
|
|
459
|
+
let variables = {};
|
|
460
|
+
|
|
461
|
+
// Handle both string and [string, variables] formats
|
|
462
|
+
if (Array.isArray(input)) {
|
|
463
|
+
[template, variables] = input;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Merge variables with inject and AWS pseudo-parameters
|
|
467
|
+
const allVariables = {
|
|
468
|
+
...getAwsPseudoParameters(),
|
|
469
|
+
...opts.inject,
|
|
470
|
+
...variables,
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// Perform substitution for ${VarName} pattern
|
|
474
|
+
let result = template.toString();
|
|
475
|
+
_.forEach(allVariables, (value, key) => {
|
|
476
|
+
const regex = new RegExp(`\\$\\{${_.escapeRegExp(key)}\\}`, 'g');
|
|
477
|
+
result = result.replace(regex, String(value));
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
return result;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
if (cft['Fn::RefNow']) {
|
|
484
|
+
return recurse({ base, scope, cft: cft['Fn::RefNow'], ...opts }).then((logicalName) => {
|
|
485
|
+
let refName = logicalName;
|
|
486
|
+
|
|
487
|
+
// Merge AWS pseudo-parameters with inject and scope variables
|
|
488
|
+
const allRefs = {
|
|
489
|
+
...getAwsPseudoParameters(),
|
|
490
|
+
...process.env,
|
|
491
|
+
...opts.inject,
|
|
492
|
+
...scope,
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// Try to resolve the reference
|
|
496
|
+
if (refName in allRefs) {
|
|
497
|
+
return allRefs[refName];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// If not found, throw an error
|
|
501
|
+
throw new Error(`Unable to resolve Ref for logical name: ${refName}`);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
444
504
|
if (cft['Fn::ApplyTags']) {
|
|
445
505
|
return recurse({ base, scope, cft: cft['Fn::ApplyTags'], ...opts }).then((json) => {
|
|
446
506
|
let { tags, Tags, resources } = json;
|
package/lib/schema.js
CHANGED
|
@@ -35,6 +35,9 @@ var tags = [
|
|
|
35
35
|
{ short: 'Eval', full: 'Fn::Eval', type: 'sequence' },
|
|
36
36
|
{ short: 'IfEval', full: 'Fn::IfEval', type: 'mapping' },
|
|
37
37
|
{ short: 'JoinNow', full: 'Fn::JoinNow', type: 'scalar' },
|
|
38
|
+
{ short: 'SubNow', full: 'Fn::SubNow', type: 'scalar' },
|
|
39
|
+
{ short: 'SubNow', full: 'Fn::SubNow', type: 'sequence' },
|
|
40
|
+
{ short: 'RefNow', full: 'Fn::RefNow', type: 'scalar' },
|
|
38
41
|
{ short: 'ApplyTags', full: 'Fn::ApplyTags', type: 'mapping' },
|
|
39
42
|
|
|
40
43
|
// http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@znemz/cfn-include",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.9",
|
|
4
4
|
"description": "Preprocessor for CloudFormation templates with support for loops and flexible include statements",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aws",
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"cloudformation",
|
|
9
9
|
"include"
|
|
10
10
|
],
|
|
11
|
-
"homepage": "https://github.com/
|
|
11
|
+
"homepage": "https://github.com/brickhouse-tech/cfn-include#readme",
|
|
12
12
|
"bugs": {
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/brickhouse-tech/cfn-include/issues"
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "
|
|
17
|
+
"url": "https://github.com/brickhouse-tech/cfn-include.git"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"author": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@aws-sdk/client-cloudformation": "^3.637.0",
|
|
44
44
|
"@aws-sdk/client-s3": "^3.637.0",
|
|
45
|
-
"@znemz/cft-utils": "0.1.
|
|
45
|
+
"@znemz/cft-utils": "0.1.30",
|
|
46
46
|
"@znemz/sort-object": "^3.0.4",
|
|
47
47
|
"aws-sdk-v3-proxy": "2.2.0",
|
|
48
48
|
"bluebird": "^3.7.2",
|
|
@@ -70,7 +70,10 @@
|
|
|
70
70
|
"npm-run-all": "4.1.5",
|
|
71
71
|
"prettier": "3",
|
|
72
72
|
"serve": "14.2.5",
|
|
73
|
-
"sort-package-json": "3.
|
|
73
|
+
"sort-package-json": "3.6.0"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=20.19"
|
|
74
77
|
},
|
|
75
78
|
"originalAuthor": {
|
|
76
79
|
"name": "Moritz Onken",
|