Skip to content

Commit

Permalink
Add JSONC parameter file support #2053 (#3170)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Nov 7, 2024
1 parent 2a9a23e commit 65ac21d
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 22 deletions.
7 changes: 7 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers

## Unreleased

- New features:
- Added support for expanding from `.jsonc` parameter files by @BernieWhite.
[#2053](https://github.com/Azure/PSRule.Rules.Azure/issues/2053)
- Previously only parameter files with the `.json` extension where automatically expanded.
- This feature adds support so that JSON parameter files with the `.jsonc` extension are also discovered and expanded.
- No additional configuration is required if expansion of JSON parameter files is enabled.
- To enable parameter file expansion set the `AZURE_PARAMETER_FILE_EXPANSION` configuration option to `true`.
- Bug fixes:
- Fixed projection of default role authorization property `principalType` by @BernieWhite.
[#3163](https://github.com/Azure/PSRule.Rules.Azure/issues/3163)
Expand Down
29 changes: 22 additions & 7 deletions docs/using-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ author: BernieWhite
PSRule for Azure discovers and analyzes Azure resources contained within template and parameter files.
To enable this feature, you need to:

- Enable expansion.
- Link parameter files to templates.
1. Enable expansion.
2. Link parameter files to templates.

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

## Linking templates

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

!!! Tip
Linking templates also applies to Bicep modules when you are using `.json` parameter files.
Linking templates also applies to Bicep modules when you are using JSON parameter files.

### By metadata

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

PSRule for Azure supports linking by naming convention when:

- Parameter files end with `.parameters.json` linking to ARM templates or Bicep modules.
- Parameter files end with `.parameters.json` or `.parameters.jsonc` linking to ARM templates or Bicep modules.
- The parameter file prefix matches the file name of the template or Bicep module.
For example, `azuredeploy.parameters.json` links to `azuredeploy.json` or `azuredeploy.bicep`.
- If both an ARM template and Bicep module exist, the template (`.json`) is preferred.
For example, `azuredeploy.parameters.json` chooses `azuredeploy.json` over `azuredeploy.bicep` if both exist.
For example, `azuredeploy.parameters.json` links to `azuredeploy.json`, `azuredeploy.jsonc`, or `azuredeploy.bicep`.
- Both parameter file and template or Bicep module must be in the same directory.
- The following search order is used to determine the file that will be used if multiple template or Bicep files exist:
1. ARM template file with the `.json` extension.
2. ARM template file with the `.jsonc` extension.
3. Bicep module file with the `.bicep` extension.

For example, `azuredeploy.parameters.json` links to `azuredeploy.json` over `azuredeploy.jsonc` or `azuredeploy.bicep`.
When `azuredeploy.json` does not exist, `azuredeploy.jsonc` is chosen.
Finally `azuredeploy.parameters.json` will link to `azuredeploy.bicep` if neither of the other files exist.

The following is not currently supported:

Expand Down
10 changes: 10 additions & 0 deletions src/PSRule.Rules.Azure/Data/Template/TemplateLinkHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal sealed class TemplateLinkHelper

private const string PARAMETER_FILE_SUFFIX = ".parameters";
private const string TEMPLATE_FILE_EXTENSION_JSON = ".json";
private const string TEMPLATE_FILE_EXTENSION_JSONC = ".jsonc";
private const string TEMPLATE_FILE_EXTENSION_BICEP = ".bicep";

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

var baseFilename = filename.Remove(filename.Length - suffix.Length);
var jsonTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_JSON));
var jsoncTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_JSONC));
var bicepTemplateFile = Path.Combine(parentPath, string.Concat(baseFilename, TEMPLATE_FILE_EXTENSION_BICEP));
if (File.Exists(jsonTemplateFile))
{
templateFile = jsonTemplateFile;
}
else if (File.Exists(jsoncTemplateFile))
{
templateFile = jsoncTemplateFile;
}
else if (File.Exists(bicepTemplateFile))
{
templateFile = bicepTemplateFile;
}

return templateFile != null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/PSRule.Rules.Azure/rules/Conventions.Rule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Export-PSRuleConvention 'Azure.Context' -Initialize {
}

# Synopsis: Expand Azure resources from parameter files.
Export-PSRuleConvention 'Azure.ExpandTemplate' -If { $Configuration.AZURE_PARAMETER_FILE_EXPANSION -eq $True -and $TargetObject.Extension -eq '.json' -and $Assert.HasJsonSchema($PSRule.GetContentFirstOrDefault($TargetObject), @(
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), @(
"https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json`#"
"https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json`#"
), $True) } -Begin {
Expand Down
2 changes: 1 addition & 1 deletion tests/PSRule.Rules.Azure.Tests/Azure.Cosmos.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Describe 'Azure.Cosmos' -Tag 'Cosmos', 'CosmosDB' {

Context 'With template' {
BeforeAll {
$templatePath = Join-Path -Path $here -ChildPath 'Resources.Cosmos.Parameters.*.json';
$templatePath = Join-Path -Path $here -ChildPath 'Resources.Cosmos.Parameters.*.json?';
$invokeParams = @{
Baseline = 'Azure.All'
Option = (Join-Path -Path $here -ChildPath 'test-template-options.yaml')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@
<None Update="Tests.Bicep.1.parameters.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="azuredeploy.jsonc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="azuredeploy.parameters.jsonc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tests.Bicep.11.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
12 changes: 0 additions & 12 deletions tests/PSRule.Rules.Azure.Tests/Resources.Cosmos.Parameters.2.json

This file was deleted.

13 changes: 13 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/Resources.Cosmos.Parameters.2.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"template": "./Resources.Cosmos.Template.json"
},
"parameters": {
// The account name of the CosmosDB account.
"accountName": {
"value": "gremlin-002"
}
}
}
19 changes: 18 additions & 1 deletion tests/PSRule.Rules.Azure.Tests/TemplateLinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void Pipeline()
}

[Fact]
public void GetBicepParameters()
public void ProcessParameterFile_WhenUsingReferencingBicep_ShouldLinkToBicep()
{
var helper = new TemplateLinkHelper(GetContext(), AppDomain.CurrentDomain.BaseDirectory, true);

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

/// <summary>
/// Test case for https://github.com/Azure/PSRule.Rules.Azure/issues/2053.
/// </summary>
[Fact]
public void ProcessParameterFile_WhenUsingJSONCExtension_ShouldLinkToTemplate()
{
var helper = new TemplateLinkHelper(GetContext(), AppDomain.CurrentDomain.BaseDirectory, true);

// From naming convention
var link = helper.ProcessParameterFile(GetSourcePath("azuredeploy.parameters.jsonc"));
Assert.NotNull(link);
Assert.NotNull(link.TemplateFile);
Assert.EndsWith("azuredeploy.jsonc", link.TemplateFile);
Assert.NotNull(link.ParameterFile);
Assert.EndsWith("azuredeploy.parameters.jsonc", link.ParameterFile);
}

#region Helper methods

private static string GetSourcePath(string fileName)
Expand Down
105 changes: 105 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/azuredeploy.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"accountName": {
"type": "string",
"metadata": {
"description": "The name of the Cosmos DB account."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location to deploy Azure resources.",
"strongType": "location"
}
},
"disableKeyBasedMetadataWriteAccess": {
"type": "bool",
"defaultValue": false,
"metadata": {
"description": "When enabled, resource keys and tokens are limit from performing resource management operations."
}
}
},
"resources": [
{
"comments": "A test Cosmos DB account.",
"type": "Microsoft.DocumentDB/databaseAccounts",
"apiVersion": "2021-04-15",
"name": "[parameters('accountName')]",
"location": "[parameters('location')]",
"tags": {
"defaultExperience": "Gremlin (graph)",
"hidden-cosmos-mmspecial": "",
"CosmosAccountType": "Production"
},
"kind": "GlobalDocumentDB",
"identity": {
"type": "None"
},
"properties": {
"publicNetworkAccess": "Enabled",
"enableAutomaticFailover": false,
"enableMultipleWriteLocations": false,
"isVirtualNetworkFilterEnabled": false,
"virtualNetworkRules": [],
"disableKeyBasedMetadataWriteAccess": "[parameters('disableKeyBasedMetadataWriteAccess')]",
"enableFreeTier": false,
"enableAnalyticalStorage": false,
"databaseAccountOfferType": "Standard",
"defaultIdentity": "FirstPartyIdentity",
"networkAclBypass": "None",
"consistencyPolicy": {
"defaultConsistencyLevel": "Session",
"maxIntervalInSeconds": 5,
"maxStalenessPrefix": 100
},
"locations": [
{
"locationName": "region",
"provisioningState": "Succeeded",
"failoverPriority": 0,
"isZoneRedundant": true
}
],
"cors": [],
"capabilities": [
{
"name": "EnableGremlin"
},
{
"name": "EnableServerless"
}
],
"ipRules": [
{
"ipAddressOrRange": "104.42.195.92"
},
{
"ipAddressOrRange": "40.76.54.131"
},
{
"ipAddressOrRange": "52.176.6.30"
},
{
"ipAddressOrRange": "52.169.50.45"
},
{
"ipAddressOrRange": "52.187.184.26"
}
],
"backupPolicy": {
"type": "Periodic",
"periodicModeProperties": {
"backupIntervalInMinutes": 240,
"backupRetentionIntervalInHours": 8
}
},
"networkAclBypassResourceIds": []
}
}
]
}
10 changes: 10 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/azuredeploy.parameters.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
// The account name of the CosmosDB account.
"accountName": {
"value": "gremlin-002"
}
}
}

0 comments on commit 65ac21d

Please sign in to comment.