Skip to content

Commit 65ac21d

Browse files
authored
Add JSONC parameter file support #2053 (#3170)
1 parent 2a9a23e commit 65ac21d

11 files changed

+193
-22
lines changed

docs/CHANGELOG-v1.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers
2929

3030
## Unreleased
3131

32+
- New features:
33+
- Added support for expanding from `.jsonc` parameter files by @BernieWhite.
34+
[#2053](https://github.com/Azure/PSRule.Rules.Azure/issues/2053)
35+
- Previously only parameter files with the `.json` extension where automatically expanded.
36+
- This feature adds support so that JSON parameter files with the `.jsonc` extension are also discovered and expanded.
37+
- No additional configuration is required if expansion of JSON parameter files is enabled.
38+
- To enable parameter file expansion set the `AZURE_PARAMETER_FILE_EXPANSION` configuration option to `true`.
3239
- Bug fixes:
3340
- Fixed projection of default role authorization property `principalType` by @BernieWhite.
3441
[#3163](https://github.com/Azure/PSRule.Rules.Azure/issues/3163)

docs/using-templates.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ author: BernieWhite
77
PSRule for Azure discovers and analyzes Azure resources contained within template and parameter files.
88
To enable this feature, you need to:
99

10-
- Enable expansion.
11-
- Link parameter files to templates.
10+
1. Enable expansion.
11+
2. Link parameter files to templates.
1212

1313
!!! Abstract
1414
This topic covers how you can validate Azure resources within template `.json` files.
@@ -24,6 +24,15 @@ configuration:
2424
AZURE_PARAMETER_FILE_EXPANSION: true
2525
```
2626
27+
After enabling expansion, PSRule for Azure will start to discover ARM templates and Bicep module from parameter files.
28+
Supported parameter files must:
29+
30+
- The use `.parameters.json` or `.parameters.jsonc` suffix. i.e. `deploy.parameters.jsonc`.
31+
- The use either of the following schemas:
32+
- `https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#` OR
33+
- `https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#`
34+
- Must be linked to a template or Bicep module using one of the methods described below.
35+
2736
## Linking templates
2837

2938
PSRule for Azure automatically detects parameter files and uses the following logic to link templates or Bicep modules.
@@ -36,7 +45,7 @@ PSRule for Azure automatically detects parameter files and uses the following lo
3645
For details on both options continue reading.
3746

3847
!!! Tip
39-
Linking templates also applies to Bicep modules when you are using `.json` parameter files.
48+
Linking templates also applies to Bicep modules when you are using JSON parameter files.
4049

4150
### By metadata
4251

@@ -123,12 +132,18 @@ When metadata links are not set, PSRule will fallback to use a naming convention
123132

124133
PSRule for Azure supports linking by naming convention when:
125134

126-
- Parameter files end with `.parameters.json` linking to ARM templates or Bicep modules.
135+
- Parameter files end with `.parameters.json` or `.parameters.jsonc` linking to ARM templates or Bicep modules.
127136
- The parameter file prefix matches the file name of the template or Bicep module.
128-
For example, `azuredeploy.parameters.json` links to `azuredeploy.json` or `azuredeploy.bicep`.
129-
- If both an ARM template and Bicep module exist, the template (`.json`) is preferred.
130-
For example, `azuredeploy.parameters.json` chooses `azuredeploy.json` over `azuredeploy.bicep` if both exist.
137+
For example, `azuredeploy.parameters.json` links to `azuredeploy.json`, `azuredeploy.jsonc`, or `azuredeploy.bicep`.
131138
- Both parameter file and template or Bicep module must be in the same directory.
139+
- The following search order is used to determine the file that will be used if multiple template or Bicep files exist:
140+
1. ARM template file with the `.json` extension.
141+
2. ARM template file with the `.jsonc` extension.
142+
3. Bicep module file with the `.bicep` extension.
143+
144+
For example, `azuredeploy.parameters.json` links to `azuredeploy.json` over `azuredeploy.jsonc` or `azuredeploy.bicep`.
145+
When `azuredeploy.json` does not exist, `azuredeploy.jsonc` is chosen.
146+
Finally `azuredeploy.parameters.json` will link to `azuredeploy.bicep` if neither of the other files exist.
132147

133148
The following is not currently supported:
134149

src/PSRule.Rules.Azure/Data/Template/TemplateLinkHelper.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal sealed class TemplateLinkHelper
2424

2525
private const string PARAMETER_FILE_SUFFIX = ".parameters";
2626
private const string TEMPLATE_FILE_EXTENSION_JSON = ".json";
27+
private const string TEMPLATE_FILE_EXTENSION_JSONC = ".jsonc";
2728
private const string TEMPLATE_FILE_EXTENSION_BICEP = ".bicep";
2829

2930
private const char SLASH = '/';
@@ -180,11 +181,20 @@ private static bool TryTemplateByName(string suffix, string parameterFile, out s
180181

181182
var baseFilename = filename.Remove(filename.Length - suffix.Length);
182183
var jsonTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_JSON));
184+
var jsoncTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_JSONC));
183185
var bicepTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_BICEP));
184186
if (File.Exists(jsonTemplateFile))
187+
{
185188
templateFile = jsonTemplateFile;
189+
}
190+
else if (File.Exists(jsoncTemplateFile))
191+
{
192+
templateFile = jsoncTemplateFile;
193+
}
186194
else if (File.Exists(bicepTemplateFile))
195+
{
187196
templateFile = bicepTemplateFile;
197+
}
188198

189199
return templateFile != null;
190200
}

src/PSRule.Rules.Azure/rules/Conventions.Rule.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Export-PSRuleConvention 'Azure.Context' -Initialize {
8585
}
8686

8787
# Synopsis: Expand Azure resources from parameter files.
88-
Export-PSRuleConvention 'Azure.ExpandTemplate' -If { $Configuration.AZURE_PARAMETER_FILE_EXPANSION -eq $True -and $TargetObject.Extension -eq '.json' -and $Assert.HasJsonSchema($PSRule.GetContentFirstOrDefault($TargetObject), @(
88+
Export-PSRuleConvention 'Azure.ExpandTemplate' -If { $Configuration.AZURE_PARAMETER_FILE_EXPANSION -eq $True -and ($TargetObject.Extension -eq '.json' -or $TargetObject.Extension -eq '.jsonc') -and $Assert.HasJsonSchema($PSRule.GetContentFirstOrDefault($TargetObject), @(
8989
"https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json`#"
9090
"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json`#"
9191
), $True) } -Begin {

tests/PSRule.Rules.Azure.Tests/Azure.Cosmos.Tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ Describe 'Azure.Cosmos' -Tag 'Cosmos', 'CosmosDB' {
173173

174174
Context 'With template' {
175175
BeforeAll {
176-
$templatePath = Join-Path -Path $here -ChildPath 'Resources.Cosmos.Parameters.*.json';
176+
$templatePath = Join-Path -Path $here -ChildPath 'Resources.Cosmos.Parameters.*.json?';
177177
$invokeParams = @{
178178
Baseline = 'Azure.All'
179179
Option = (Join-Path -Path $here -ChildPath 'test-template-options.yaml')

tests/PSRule.Rules.Azure.Tests/PSRule.Rules.Azure.Tests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@
176176
<None Update="Tests.Bicep.1.parameters.json">
177177
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
178178
</None>
179+
<None Update="azuredeploy.jsonc">
180+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
181+
</None>
182+
<None Update="azuredeploy.parameters.jsonc">
183+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
184+
</None>
179185
<None Update="Tests.Bicep.11.json">
180186
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
181187
</None>

tests/PSRule.Rules.Azure.Tests/Resources.Cosmos.Parameters.2.json

Lines changed: 0 additions & 12 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"metadata": {
5+
"template": "./Resources.Cosmos.Template.json"
6+
},
7+
"parameters": {
8+
// The account name of the CosmosDB account.
9+
"accountName": {
10+
"value": "gremlin-002"
11+
}
12+
}
13+
}

tests/PSRule.Rules.Azure.Tests/TemplateLinkTests.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void Pipeline()
2323
}
2424

2525
[Fact]
26-
public void GetBicepParameters()
26+
public void ProcessParameterFile_WhenUsingReferencingBicep_ShouldLinkToBicep()
2727
{
2828
var helper = new TemplateLinkHelper(GetContext(), AppDomain.CurrentDomain.BaseDirectory, true);
2929

@@ -52,6 +52,23 @@ public void GetBicepParameters()
5252
Assert.EndsWith("Tests.Bicep.2.parameters.json", link.ParameterFile);
5353
}
5454

55+
/// <summary>
56+
/// Test case for https://github.com/Azure/PSRule.Rules.Azure/issues/2053.
57+
/// </summary>
58+
[Fact]
59+
public void ProcessParameterFile_WhenUsingJSONCExtension_ShouldLinkToTemplate()
60+
{
61+
var helper = new TemplateLinkHelper(GetContext(), AppDomain.CurrentDomain.BaseDirectory, true);
62+
63+
// From naming convention
64+
var link = helper.ProcessParameterFile(GetSourcePath("azuredeploy.parameters.jsonc"));
65+
Assert.NotNull(link);
66+
Assert.NotNull(link.TemplateFile);
67+
Assert.EndsWith("azuredeploy.jsonc", link.TemplateFile);
68+
Assert.NotNull(link.ParameterFile);
69+
Assert.EndsWith("azuredeploy.parameters.jsonc", link.ParameterFile);
70+
}
71+
5572
#region Helper methods
5673

5774
private static string GetSourcePath(string fileName)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"accountName": {
6+
"type": "string",
7+
"metadata": {
8+
"description": "The name of the Cosmos DB account."
9+
}
10+
},
11+
"location": {
12+
"type": "string",
13+
"defaultValue": "[resourceGroup().location]",
14+
"metadata": {
15+
"description": "The location to deploy Azure resources.",
16+
"strongType": "location"
17+
}
18+
},
19+
"disableKeyBasedMetadataWriteAccess": {
20+
"type": "bool",
21+
"defaultValue": false,
22+
"metadata": {
23+
"description": "When enabled, resource keys and tokens are limit from performing resource management operations."
24+
}
25+
}
26+
},
27+
"resources": [
28+
{
29+
"comments": "A test Cosmos DB account.",
30+
"type": "Microsoft.DocumentDB/databaseAccounts",
31+
"apiVersion": "2021-04-15",
32+
"name": "[parameters('accountName')]",
33+
"location": "[parameters('location')]",
34+
"tags": {
35+
"defaultExperience": "Gremlin (graph)",
36+
"hidden-cosmos-mmspecial": "",
37+
"CosmosAccountType": "Production"
38+
},
39+
"kind": "GlobalDocumentDB",
40+
"identity": {
41+
"type": "None"
42+
},
43+
"properties": {
44+
"publicNetworkAccess": "Enabled",
45+
"enableAutomaticFailover": false,
46+
"enableMultipleWriteLocations": false,
47+
"isVirtualNetworkFilterEnabled": false,
48+
"virtualNetworkRules": [],
49+
"disableKeyBasedMetadataWriteAccess": "[parameters('disableKeyBasedMetadataWriteAccess')]",
50+
"enableFreeTier": false,
51+
"enableAnalyticalStorage": false,
52+
"databaseAccountOfferType": "Standard",
53+
"defaultIdentity": "FirstPartyIdentity",
54+
"networkAclBypass": "None",
55+
"consistencyPolicy": {
56+
"defaultConsistencyLevel": "Session",
57+
"maxIntervalInSeconds": 5,
58+
"maxStalenessPrefix": 100
59+
},
60+
"locations": [
61+
{
62+
"locationName": "region",
63+
"provisioningState": "Succeeded",
64+
"failoverPriority": 0,
65+
"isZoneRedundant": true
66+
}
67+
],
68+
"cors": [],
69+
"capabilities": [
70+
{
71+
"name": "EnableGremlin"
72+
},
73+
{
74+
"name": "EnableServerless"
75+
}
76+
],
77+
"ipRules": [
78+
{
79+
"ipAddressOrRange": "104.42.195.92"
80+
},
81+
{
82+
"ipAddressOrRange": "40.76.54.131"
83+
},
84+
{
85+
"ipAddressOrRange": "52.176.6.30"
86+
},
87+
{
88+
"ipAddressOrRange": "52.169.50.45"
89+
},
90+
{
91+
"ipAddressOrRange": "52.187.184.26"
92+
}
93+
],
94+
"backupPolicy": {
95+
"type": "Periodic",
96+
"periodicModeProperties": {
97+
"backupIntervalInMinutes": 240,
98+
"backupRetentionIntervalInHours": 8
99+
}
100+
},
101+
"networkAclBypassResourceIds": []
102+
}
103+
}
104+
]
105+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
// The account name of the CosmosDB account.
6+
"accountName": {
7+
"value": "gremlin-002"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)