From 14acda482730f5ab91315a104e117b2336ca0cd8 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Thu, 27 Oct 2022 21:11:54 +0200 Subject: [PATCH 01/24] add auto discovery of parameter files when scanning directories --- .../CommandLineParserTests.cs | 17 ++++++++++++++++- .../TemplateWithSeparateParametersFile.bicep | 7 +++++++ ...teWithSeparateParametersFile.parameters.json | 13 +++++++++++++ src/Analyzer.Cli/CommandLineParser.cs | 10 +++++++++- 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.bicep create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters.json diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index b1f87462..88bae044 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -110,7 +110,22 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFiles() var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.ErrorAndViolation, result.Result); - StringAssert.Contains(outputWriter.ToString(), "Analyzed 10 files"); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 11 files"); + } + + [TestMethod] + public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithParameters() + { + var args = new string[] { "analyze-directory", Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile") }; + + using StringWriter outputWriter = new(); + Console.SetOut(outputWriter); + + var result = _commandLineParser.InvokeCommandLineAPIAsync(args); + + Assert.AreEqual((int)ExitCode.Success, result.Result); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 1 file"); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters.json")); } [DataTestMethod] diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.bicep b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.bicep new file mode 100644 index 00000000..84c79b6f --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.bicep @@ -0,0 +1,7 @@ +param infra object + +resource applicationInsight 'microsoft.insights/components@2020-02-02' = { + name: 'test' + location: infra.environment.location + kind: 'other' +} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters.json new file mode 100644 index 00000000..d043d2a0 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "value": { + "environment": { + "location": "West Europe" + } + } + } + } +} \ No newline at end of file diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 698b3049..04d09ad8 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -236,7 +236,15 @@ private int AnalyzeDirectoryCommandHandler( var filesFailed = new List(); foreach (FileInfo file in filesToAnalyze) { - ExitCode res = AnalyzeTemplate(file, null); + // Check if a {template}.parameters.json file is present, and if so use it as the parametersFile input + FileInfo parametersFile = null; + string defaultParametersFileLocation = Path.Combine(file.Directory.FullName, Path.GetFileNameWithoutExtension(file.Name) + ".parameters.json"); + if (File.Exists(defaultParametersFileLocation)) + { + parametersFile = new FileInfo(defaultParametersFileLocation); + } + + ExitCode res = AnalyzeTemplate(file, parametersFile); if (res == ExitCode.Success || res == ExitCode.Violation) { From 34c1492ccd268628ee5600beeea8e11325352dfd Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Fri, 28 Oct 2022 09:20:40 +0200 Subject: [PATCH 02/24] Moved auto detect parameters file functionality to separate method, also implemented this in AnalyzeTemplateCommandHandler, added test. --- .../CommandLineParserTests.cs | 18 +++++++++++++- src/Analyzer.Cli/CommandLineParser.cs | 24 +++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index 2522e050..099cb3db 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -99,6 +99,22 @@ public void AnalyzeTemplate_IncludesOrNotNonSecurityRules_ReturnsExpectedExitCod Assert.AreEqual((int)ExitCode.Violation, result.Result); } + [TestMethod] + public void AnalyzeTemplate_ValidInputValues_AnalyzesUsingAutoDetectedParameters() + { + var templatePath = GetFilePath(Path.Combine("ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.bicep")); + + var args = new string[] { "analyze-template", templatePath }; + + using StringWriter outputWriter = new(); + Console.SetOut(outputWriter); + + var result = _commandLineParser.InvokeCommandLineAPIAsync(args); + + Assert.AreEqual((int)ExitCode.Success, result.Result); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters.json")); + } + [TestMethod] public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFiles() { @@ -114,7 +130,7 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFiles() } [TestMethod] - public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithParameters() + public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithAutoDetectedParameters() { var args = new string[] { "analyze-directory", Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile") }; diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 1ae5da71..d33f25ff 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -198,7 +198,10 @@ private int AnalyzeTemplateCommandHandler( return (int)ExitCode.ErrorInvalidARMTemplate; } - var analysisResult = AnalyzeTemplate(templateFilePath, parametersFilePath); + // If a parameters file is not supplied, check if a {template}.parameters.json file is present, and if so use it as the parametersFile input + FileInfo parametersFile = parametersFilePath == null ? FindParameterFileForTemplate(templateFilePath) : parametersFilePath; + + var analysisResult = AnalyzeTemplate(templateFilePath, parametersFile); FinishAnalysis(); return (int)analysisResult; @@ -237,12 +240,7 @@ private int AnalyzeDirectoryCommandHandler( foreach (FileInfo file in filesToAnalyze) { // Check if a {template}.parameters.json file is present, and if so use it as the parametersFile input - FileInfo parametersFile = null; - string defaultParametersFileLocation = Path.Combine(file.Directory.FullName, Path.GetFileNameWithoutExtension(file.Name) + ".parameters.json"); - if (File.Exists(defaultParametersFileLocation)) - { - parametersFile = new FileInfo(defaultParametersFileLocation); - } + FileInfo parametersFile = FindParameterFileForTemplate(file); ExitCode res = AnalyzeTemplate(file, parametersFile); @@ -361,6 +359,18 @@ private IEnumerable FindTemplateFilesInDirectory(DirectoryInfo directo return armTemplates.Concat(bicepTemplates); } + private FileInfo FindParameterFileForTemplate(FileInfo template) + { + FileInfo parametersFile = null; + string defaultParametersFileLocation = Path.Combine(template.Directory.FullName, Path.GetFileNameWithoutExtension(template.Name) + ".parameters.json"); + if (File.Exists(defaultParametersFileLocation)) + { + parametersFile = new FileInfo(defaultParametersFileLocation); + } + + return parametersFile; + } + private bool IsValidTemplate(FileInfo file) { // assume bicep files are valid, they are compiled/verified later From b348f205123045cb063639db6eed379ad5502301 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Mon, 31 Oct 2022 11:54:37 +0100 Subject: [PATCH 03/24] check for `.parameters*.json` so we also support the parameters file naming standard where environment are defined (for example `azuredeploy.parameters-dev.json`) --- .../CommandLineParserTests.cs | 8 +- ...SeparateParametersFile.parameters-acc.json | 13 +++ ...SeparateParametersFile.parameters-dev.json | 13 +++ ...SeparateParametersFile.parameters-prd.json | 13 +++ ...SeparateParametersFile.parameters-tst.json | 13 +++ src/Analyzer.Cli/CommandLineParser.cs | 107 ++++++++++++++---- 6 files changed, 143 insertions(+), 24 deletions(-) create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-dev.json create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index 099cb3db..0cde787c 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -126,7 +126,7 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFiles() var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.ErrorAndViolation, result.Result); - StringAssert.Contains(outputWriter.ToString(), "Analyzed 11 files"); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 15 files"); } [TestMethod] @@ -140,8 +140,12 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithA var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.Success, result.Result); - StringAssert.Contains(outputWriter.ToString(), "Analyzed 1 file"); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 5 files"); StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters.json")); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-dev.json")); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-tst.json")); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-acc.json")); + StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-prd.json")); } [DataTestMethod] diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json new file mode 100644 index 00000000..d043d2a0 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "value": { + "environment": { + "location": "West Europe" + } + } + } + } +} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-dev.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-dev.json new file mode 100644 index 00000000..d043d2a0 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-dev.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "value": { + "environment": { + "location": "West Europe" + } + } + } + } +} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json new file mode 100644 index 00000000..d043d2a0 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "value": { + "environment": { + "location": "West Europe" + } + } + } + } +} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json new file mode 100644 index 00000000..d043d2a0 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "value": { + "environment": { + "location": "West Europe" + } + } + } + } +} \ No newline at end of file diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index d33f25ff..c17e6875 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -198,13 +198,56 @@ private int AnalyzeTemplateCommandHandler( return (int)ExitCode.ErrorInvalidARMTemplate; } - // If a parameters file is not supplied, check if a {template}.parameters.json file is present, and if so use it as the parametersFile input - FileInfo parametersFile = parametersFilePath == null ? FindParameterFileForTemplate(templateFilePath) : parametersFilePath; + ExitCode exitCode; + if (parametersFilePath != null) + { + exitCode = AnalyzeTemplate(templateFilePath, parametersFilePath); + } + else + { + // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input + var parametersFiles = FindParameterFileForTemplate(templateFilePath); + int numOfFilesAnalyzed = 0; + bool issueReported = false; + var filesFailed = new List(); + + if (parametersFiles.Count() > 0) + { + foreach (FileInfo parametersFile in parametersFiles) + { + ExitCode res = AnalyzeTemplate(templateFilePath, parametersFile); + + if (res == ExitCode.Success || res == ExitCode.Violation) + { + numOfFilesAnalyzed++; + issueReported |= res == ExitCode.Violation; + } + else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) + { + filesFailed.Add(templateFilePath); + } + } - var analysisResult = AnalyzeTemplate(templateFilePath, parametersFile); + Console.WriteLine(Environment.NewLine + $"Analyzed {numOfFilesAnalyzed} {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); + + if (filesFailed.Count > 0) + { + logger.LogError($"Unable to analyze {filesFailed.Count} {(filesFailed.Count == 1 ? "file" : "files")}: {string.Join(", ", filesFailed)}"); + exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; + } + else + { + exitCode = issueReported ? ExitCode.Violation : ExitCode.Success; + } + } + else + { + exitCode = AnalyzeTemplate(templateFilePath, null); + } + } FinishAnalysis(); - return (int)analysisResult; + return (int)exitCode; } // Note: argument names must match command arguments/options (without "-" characters) @@ -239,19 +282,38 @@ private int AnalyzeDirectoryCommandHandler( var filesFailed = new List(); foreach (FileInfo file in filesToAnalyze) { - // Check if a {template}.parameters.json file is present, and if so use it as the parametersFile input - FileInfo parametersFile = FindParameterFileForTemplate(file); - - ExitCode res = AnalyzeTemplate(file, parametersFile); - - if (res == ExitCode.Success || res == ExitCode.Violation) + // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input + var parametersFiles = FindParameterFileForTemplate(file); + if (parametersFiles.Count() > 0) { - numOfFilesAnalyzed++; - issueReported |= res == ExitCode.Violation; + foreach (FileInfo parametersFile in parametersFiles) + { + ExitCode res = AnalyzeTemplate(file, parametersFile); + + if (res == ExitCode.Success || res == ExitCode.Violation) + { + numOfFilesAnalyzed++; + issueReported |= res == ExitCode.Violation; + } + else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) + { + filesFailed.Add(file); + } + } } - else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) + else { - filesFailed.Add(file); + ExitCode res = AnalyzeTemplate(file, null); + + if (res == ExitCode.Success || res == ExitCode.Violation) + { + numOfFilesAnalyzed++; + issueReported |= res == ExitCode.Violation; + } + else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) + { + filesFailed.Add(file); + } } } @@ -359,16 +421,17 @@ private IEnumerable FindTemplateFilesInDirectory(DirectoryInfo directo return armTemplates.Concat(bicepTemplates); } - private FileInfo FindParameterFileForTemplate(FileInfo template) + private IEnumerable FindParameterFileForTemplate(FileInfo template) { - FileInfo parametersFile = null; - string defaultParametersFileLocation = Path.Combine(template.Directory.FullName, Path.GetFileNameWithoutExtension(template.Name) + ".parameters.json"); - if (File.Exists(defaultParametersFileLocation)) - { - parametersFile = new FileInfo(defaultParametersFileLocation); - } + var parametersFiles = template.Directory.GetFiles( + Path.GetFileNameWithoutExtension(template.Name) + ".parameters*.json", + new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = false + }); - return parametersFile; + return parametersFiles; } private bool IsValidTemplate(FileInfo file) From 49eff744f7de933e879528c6e9c096fde5e8edee Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Mon, 31 Oct 2022 11:54:47 +0100 Subject: [PATCH 04/24] update documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 89e0e3a6..fdfe05f4 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Argument | Description **(Optional)** `-v` or `--verbose` | Shows details about the analysis **(Optional)** `--include-non-security-rules` | Run all the rules against the templates, including non-security rules - The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. + The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA will check if a template with the [general naming standards defined by Microsoft](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name) is present in the folder, otherwise it generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. **Note**: Providing the Template BPA with template parameter values will result in more accurate results as it will more accurately represent your deployments. The values provided to parameters may affect the evaluation of the Template BPA rule, altering its results. That said, **DO NOT** save sensitive data (passwords, connection strings, etc.) in parameter files in your repositories. Instead, [retrieve these values from your template from Azure Key Vault](https://docs.microsoft.com/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-static-id). From a8c042abba755dc15362c6e5065fcc91df3c4de2 Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Mon, 31 Oct 2022 20:30:56 +0100 Subject: [PATCH 05/24] Update logging according to code suggestion Co-authored-by: Vera Bogdanich Espina --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index c17e6875..33c4d988 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -228,7 +228,7 @@ private int AnalyzeTemplateCommandHandler( } } - Console.WriteLine(Environment.NewLine + $"Analyzed {numOfFilesAnalyzed} {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); + Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); if (filesFailed.Count > 0) { From 9967bff6ea59906053eff3b4dba913abf1ce9768 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 08:03:16 +0100 Subject: [PATCH 06/24] removed excessive test files, added TemplateWithSeparateParametersFile.json test file --- .../Analyzer.Cli.FunctionalTests.csproj | 3 +++ .../CommandLineParserTests.cs | 7 ++----- .../TemplateWithSeparateParametersFile.json | 21 +++++++++++++++++++ ...SeparateParametersFile.parameters-acc.json | 13 ------------ ...SeparateParametersFile.parameters-prd.json | 13 ------------ ...SeparateParametersFile.parameters-tst.json | 13 ------------ 6 files changed, 26 insertions(+), 44 deletions(-) create mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.json delete mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json delete mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json delete mode 100644 src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json diff --git a/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj b/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj index 37633bbe..711308db 100644 --- a/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj +++ b/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj @@ -28,6 +28,9 @@ PreserveNewest + + PreserveNewest + \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index 0cde787c..dd6e71ed 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -126,7 +126,7 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFiles() var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.ErrorAndViolation, result.Result); - StringAssert.Contains(outputWriter.ToString(), "Analyzed 15 files"); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 14 files"); } [TestMethod] @@ -140,12 +140,9 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithA var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.Success, result.Result); - StringAssert.Contains(outputWriter.ToString(), "Analyzed 5 files"); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 4 files"); StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters.json")); StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-dev.json")); - StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-tst.json")); - StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-acc.json")); - StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-prd.json")); } [DataTestMethod] diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.json new file mode 100644 index 00000000..d59a6dd9 --- /dev/null +++ b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "infra": { + "type": "object", + "metadata": { + "description": "Location for the resource." + } + } + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "test", + "location": "[parameters('infra').environment.location]", + "kind": "other" + } + ] +} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json deleted file mode 100644 index d043d2a0..00000000 --- a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-acc.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "infra": { - "value": { - "environment": { - "location": "West Europe" - } - } - } - } -} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json deleted file mode 100644 index d043d2a0..00000000 --- a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-prd.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "infra": { - "value": { - "environment": { - "location": "West Europe" - } - } - } - } -} \ No newline at end of file diff --git a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json b/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json deleted file mode 100644 index d043d2a0..00000000 --- a/src/Analyzer.Cli.FunctionalTests/Tests/ToTestSeparateParametersFile/TemplateWithSeparateParametersFile.parameters-tst.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "infra": { - "value": { - "environment": { - "location": "West Europe" - } - } - } - } -} \ No newline at end of file From 881b3ada7810321ca1ab57306b974464a531c546 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 08:18:53 +0100 Subject: [PATCH 07/24] added assertion to check the amount of times the bicep and json files are tested --- src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index dd6e71ed..a94c551a 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -8,7 +8,7 @@ using Microsoft.Azure.Templates.Analyzer.Cli; using Microsoft.Azure.Templates.Analyzer.Types; using Newtonsoft.Json.Linq; - +using System.Text.RegularExpressions; namespace Analyzer.Cli.FunctionalTests { @@ -140,9 +140,12 @@ public void AnalyzeDirectory_ValidInputValues_AnalyzesExpectedNumberOfFilesWithA var result = _commandLineParser.InvokeCommandLineAPIAsync(args); Assert.AreEqual((int)ExitCode.Success, result.Result); + StringAssert.Contains(outputWriter.ToString(), "Analyzed 4 files"); StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters.json")); StringAssert.Contains(outputWriter.ToString(), "Parameters File: " + Path.Combine(Directory.GetCurrentDirectory(), "Tests", "ToTestSeparateParametersFile", "TemplateWithSeparateParametersFile.parameters-dev.json")); + Assert.AreEqual(2, Regex.Matches(outputWriter.ToString(), "TemplateWithSeparateParametersFile.bicep").Count); + Assert.AreEqual(2, Regex.Matches(outputWriter.ToString(), "TemplateWithSeparateParametersFile.json").Count); } [DataTestMethod] From ff69599852c90d50b58726f371e060cb3049666d Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 09:09:06 +0100 Subject: [PATCH 08/24] removed duplicate logging, exclude paramaters files from FindTemplateFilesInDirectory --- src/Analyzer.Cli/CommandLineParser.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 33c4d988..d4af6544 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -232,7 +232,6 @@ private int AnalyzeTemplateCommandHandler( if (filesFailed.Count > 0) { - logger.LogError($"Unable to analyze {filesFailed.Count} {(filesFailed.Count == 1 ? "file" : "files")}: {string.Join(", ", filesFailed)}"); exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; } else @@ -322,7 +321,6 @@ private int AnalyzeDirectoryCommandHandler( ExitCode exitCode; if (filesFailed.Count > 0) { - logger.LogError($"Unable to analyze {filesFailed.Count} {(filesFailed.Count == 1 ? "file" : "files")}: {string.Join(", ", filesFailed)}"); exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; } else @@ -350,7 +348,14 @@ private ExitCode AnalyzeTemplate(FileInfo templateFilePath, FileInfo parametersF } catch (Exception exception) { - logger.LogError(exception, "An exception occurred while analyzing a template"); + if (parametersFilePath != null) + { + logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name} and parameters file {parametersFilePath.Name}"); + } + else + { + logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name}"); + } return (exception.Message == TemplateAnalyzer.BicepCompileErrorMessage) ? ExitCode.ErrorInvalidBicepTemplate @@ -408,7 +413,7 @@ private IEnumerable FindTemplateFilesInDirectory(DirectoryInfo directo MatchCasing = MatchCasing.CaseInsensitive, RecurseSubdirectories = true } - ).Where(IsValidTemplate); + ).Where(s => !s.Name.Contains(".parameters")).Where(IsValidTemplate); var bicepTemplates = directoryPath.GetFiles( "*.bicep", From a3b3fd585f3c91fb4b22404efad5869c986a6cf7 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 09:49:05 +0100 Subject: [PATCH 09/24] refactored duplicate code --- src/Analyzer.Cli/CommandLineParser.cs | 89 +++++++++------------------ 1 file changed, 29 insertions(+), 60 deletions(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index d4af6544..eda31b40 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -207,37 +207,17 @@ private int AnalyzeTemplateCommandHandler( { // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input var parametersFiles = FindParameterFileForTemplate(templateFilePath); - int numOfFilesAnalyzed = 0; - bool issueReported = false; - var filesFailed = new List(); if (parametersFiles.Count() > 0) { + List exitCodes = new List(); + foreach (FileInfo parametersFile in parametersFiles) { - ExitCode res = AnalyzeTemplate(templateFilePath, parametersFile); - - if (res == ExitCode.Success || res == ExitCode.Violation) - { - numOfFilesAnalyzed++; - issueReported |= res == ExitCode.Violation; - } - else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) - { - filesFailed.Add(templateFilePath); - } + exitCodes.Add(AnalyzeTemplate(templateFilePath, parametersFile)); } - Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); - - if (filesFailed.Count > 0) - { - exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; - } - else - { - exitCode = issueReported ? ExitCode.Violation : ExitCode.Success; - } + exitCode = AnalyzeExitCodesAndNumberOfFilesAnalyzed(exitCodes); } else { @@ -276,9 +256,7 @@ private int AnalyzeDirectoryCommandHandler( // Log root directory info to be analyzed Console.WriteLine(Environment.NewLine + Environment.NewLine + $"Directory: {directoryPath}"); - int numOfFilesAnalyzed = 0; - bool issueReported = false; - var filesFailed = new List(); + List exitCodes = new List(); foreach (FileInfo file in filesToAnalyze) { // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input @@ -287,46 +265,16 @@ private int AnalyzeDirectoryCommandHandler( { foreach (FileInfo parametersFile in parametersFiles) { - ExitCode res = AnalyzeTemplate(file, parametersFile); - - if (res == ExitCode.Success || res == ExitCode.Violation) - { - numOfFilesAnalyzed++; - issueReported |= res == ExitCode.Violation; - } - else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) - { - filesFailed.Add(file); - } + exitCodes.Add(AnalyzeTemplate(file, parametersFile)); } } else { - ExitCode res = AnalyzeTemplate(file, null); - - if (res == ExitCode.Success || res == ExitCode.Violation) - { - numOfFilesAnalyzed++; - issueReported |= res == ExitCode.Violation; - } - else if (res == ExitCode.ErrorAnalysis || res == ExitCode.ErrorInvalidBicepTemplate) - { - filesFailed.Add(file); - } + exitCodes.Add(AnalyzeTemplate(file, null)); } } - Console.WriteLine(Environment.NewLine + $"Analyzed {numOfFilesAnalyzed} {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); - - ExitCode exitCode; - if (filesFailed.Count > 0) - { - exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; - } - else - { - exitCode = issueReported ? ExitCode.Violation : ExitCode.Success; - } + ExitCode exitCode = AnalyzeExitCodesAndNumberOfFilesAnalyzed(exitCodes); FinishAnalysis(); @@ -576,5 +524,26 @@ private bool TryReadConfigurationFile(FileInfo configurationFile, out Configurat return false; } } + + private ExitCode AnalyzeExitCodesAndNumberOfFilesAnalyzed(List exitCodes) + { + int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); + Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); + + ExitCode exitCode; + bool issueReported = exitCodes.Where(x => x == ExitCode.Violation).Count() > 0; + bool filesFailed = exitCodes.Where(x => x == ExitCode.ErrorAnalysis || x == ExitCode.ErrorInvalidBicepTemplate).Count() > 0; + + if (filesFailed) + { + exitCode = issueReported ? ExitCode.ErrorAndViolation : ExitCode.ErrorAnalysis; + } + else + { + exitCode = issueReported ? ExitCode.Violation : ExitCode.Success; + } + + return exitCode; + } } } \ No newline at end of file From 10912d7b7f451195cae7f35342b396e30ea5017d Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 10:08:47 +0100 Subject: [PATCH 10/24] changed up logging --- src/Analyzer.Cli/CommandLineParser.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index eda31b40..d31c433e 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -207,22 +207,24 @@ private int AnalyzeTemplateCommandHandler( { // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input var parametersFiles = FindParameterFileForTemplate(templateFilePath); + List exitCodes = new List(); if (parametersFiles.Count() > 0) { - List exitCodes = new List(); - foreach (FileInfo parametersFile in parametersFiles) { exitCodes.Add(AnalyzeTemplate(templateFilePath, parametersFile)); } - exitCode = AnalyzeExitCodesAndNumberOfFilesAnalyzed(exitCodes); + exitCode = AnalyzeExitCodes(exitCodes); } else { exitCode = AnalyzeTemplate(templateFilePath, null); } + + int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); + Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); } FinishAnalysis(); @@ -274,7 +276,10 @@ private int AnalyzeDirectoryCommandHandler( } } - ExitCode exitCode = AnalyzeExitCodesAndNumberOfFilesAnalyzed(exitCodes); + int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); + Console.WriteLine(Environment.NewLine + $"Analyzed {numOfFilesAnalyzed} {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); + + ExitCode exitCode = AnalyzeExitCodes(exitCodes); FinishAnalysis(); @@ -525,11 +530,8 @@ private bool TryReadConfigurationFile(FileInfo configurationFile, out Configurat } } - private ExitCode AnalyzeExitCodesAndNumberOfFilesAnalyzed(List exitCodes) + private ExitCode AnalyzeExitCodes(List exitCodes) { - int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); - Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); - ExitCode exitCode; bool issueReported = exitCodes.Where(x => x == ExitCode.Violation).Count() > 0; bool filesFailed = exitCodes.Where(x => x == ExitCode.ErrorAnalysis || x == ExitCode.ErrorInvalidBicepTemplate).Count() > 0; From d85ae75a45fff7c3f2f2c0fabbcd2309dca14f4c Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 10:27:16 +0100 Subject: [PATCH 11/24] changed tests according to logging changes --- .../CommandLineParserTests.cs | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index a94c551a..06785e77 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -178,24 +178,14 @@ public void AnalyzeDirectory_DirectoryWithInvalidTemplates_LogsExpectedErrorInSa var sarifOutput = JObject.Parse(File.ReadAllText(outputFilePath)); var toolNotifications = sarifOutput["runs"][0]["invocations"][0]["toolExecutionNotifications"]; - var templateErrorMessage = "An exception occurred while analyzing a template"; - Assert.AreEqual(templateErrorMessage, toolNotifications[0]["message"]["text"]); - Assert.AreEqual(templateErrorMessage, toolNotifications[1]["message"]["text"]); - - var nonJsonFilePath1 = Path.Combine(directoryToAnalyze, "AnInvalidTemplate.json"); - var nonJsonFilePath2 = Path.Combine(directoryToAnalyze, "AnInvalidTemplate.bicep"); - var thirdNotificationMessageText = toolNotifications[2]["message"]["text"].ToString(); - // Both orders have to be considered for Windows and Linux: - Assert.IsTrue($"Unable to analyze 2 files: {nonJsonFilePath1}, {nonJsonFilePath2}" == thirdNotificationMessageText || - $"Unable to analyze 2 files: {nonJsonFilePath2}, {nonJsonFilePath1}" == thirdNotificationMessageText); - + Assert.AreEqual("An exception occurred while analyzing template AnInvalidTemplate.json", toolNotifications[0]["message"]["text"]); + Assert.AreEqual("An exception occurred while analyzing template AnInvalidTemplate.bicep", toolNotifications[1]["message"]["text"]); + Assert.AreEqual("error", toolNotifications[0]["level"]); Assert.AreEqual("error", toolNotifications[1]["level"]); - Assert.AreEqual("error", toolNotifications[2]["level"]); Assert.AreNotEqual(null, toolNotifications[0]["exception"]); Assert.AreNotEqual(null, toolNotifications[1]["exception"]); - Assert.AreEqual(null, toolNotifications[2]["exception"]); } [DataTestMethod] @@ -214,12 +204,24 @@ public void AnalyzeDirectory_ExecutionWithErrorAndWarning_PrintsExpectedMessages } var warningMessage = "An exception occurred when processing the template language expressions"; - var errorMessage = "An exception occurred while analyzing a template"; + var errorMessage1 = "An exception occurred while analyzing template ReportsError.json"; + var errorMessage2 = "An exception occurred while analyzing template ReportsError2.json"; - expectedLogSummary += ($"{Environment.NewLine}{Environment.NewLine}\tSummary of the warnings:" + - $"{Environment.NewLine}\t\t1 instance of: {warningMessage}{Environment.NewLine}") + - $"{Environment.NewLine}\tSummary of the errors:" + - $"{Environment.NewLine}\t\t{(multipleErrors ? "2 instances" : "1 instance")} of: {errorMessage}"; + if (!multipleErrors) + { + expectedLogSummary += ($"{Environment.NewLine}{Environment.NewLine}\tSummary of the warnings:" + + $"{Environment.NewLine}\t\t1 instance of: {warningMessage}{Environment.NewLine}") + + $"{Environment.NewLine}\tSummary of the errors:" + + $"{Environment.NewLine}\t\t1 instance of: {errorMessage1}"; + } + else + { + expectedLogSummary += ($"{Environment.NewLine}{Environment.NewLine}\tSummary of the warnings:" + + $"{Environment.NewLine}\t\t1 instance of: {warningMessage}{Environment.NewLine}") + + $"{Environment.NewLine}\tSummary of the errors:" + + $"{Environment.NewLine}\t\t1 instance of: {errorMessage1}" + + $"{Environment.NewLine}\t\t1 instance of: {errorMessage2}"; + } expectedLogSummary += ($"{Environment.NewLine}{Environment.NewLine}\t1 Warning" + $"{Environment.NewLine}\t{(multipleErrors ? "2 Errors" : "1 Error")}{Environment.NewLine}"); @@ -249,7 +251,7 @@ public void AnalyzeDirectory_ExecutionWithErrorAndWarning_PrintsExpectedMessages var indexOfLogSummary = cliConsoleOutput.IndexOf("Execution summary:"); Assert.IsTrue(indexOfLogSummary >= 0, $"Expected log message not found in CLI output. Found:{Environment.NewLine}{cliConsoleOutput}"); - var errorLog = $"Error: {errorMessage}"; + var errorLog = $"Error: {errorMessage1}"; var warningLog = $"Warning: {warningMessage}"; if (usesVerboseMode) { From 7e37cb21d45a3e459ea3c5143e6811d27aa36162 Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:39:21 +0100 Subject: [PATCH 12/24] Update README.md Co-authored-by: Vera Bogdanich Espina --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdfe05f4..f57f5364 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Argument | Description **(Optional)** `-v` or `--verbose` | Shows details about the analysis **(Optional)** `--include-non-security-rules` | Run all the rules against the templates, including non-security rules - The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA will check if a template with the [general naming standards defined by Microsoft](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name) is present in the folder, otherwise it generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. + The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA will check if templates with the [general naming standards defined by Microsoft](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name) are present in the folder, otherwise it generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. **Note**: Providing the Template BPA with template parameter values will result in more accurate results as it will more accurately represent your deployments. The values provided to parameters may affect the evaluation of the Template BPA rule, altering its results. That said, **DO NOT** save sensitive data (passwords, connection strings, etc.) in parameter files in your repositories. Instead, [retrieve these values from your template from Azure Key Vault](https://docs.microsoft.com/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-static-id). From 473ad37912a180cbe3d5cf97ebe18c408be534e8 Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:39:41 +0100 Subject: [PATCH 13/24] Update src/Analyzer.Cli/CommandLineParser.cs Co-authored-by: Vera Bogdanich Espina --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index d31c433e..58e71798 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -303,7 +303,7 @@ private ExitCode AnalyzeTemplate(FileInfo templateFilePath, FileInfo parametersF { if (parametersFilePath != null) { - logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name} and parameters file {parametersFilePath.Name}"); + logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name} with parameters file {parametersFilePath.Name}"); } else { From 5718325559dc7b595aa050102a66b33684e1dc30 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 19:47:59 +0100 Subject: [PATCH 14/24] reorder usings --- src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index 06785e77..0dfb7f2a 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -4,11 +4,11 @@ using System; using System.Linq; using System.IO; +using System.Text.RegularExpressions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Azure.Templates.Analyzer.Cli; using Microsoft.Azure.Templates.Analyzer.Types; using Newtonsoft.Json.Linq; -using System.Text.RegularExpressions; namespace Analyzer.Cli.FunctionalTests { From d3dd96d32e7b79c8c7bd89fe54d9909a95c53700 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 19:50:14 +0100 Subject: [PATCH 15/24] revert change in csproj --- .../Analyzer.Cli.FunctionalTests.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj b/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj index 711308db..37633bbe 100644 --- a/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj +++ b/src/Analyzer.Cli.FunctionalTests/Analyzer.Cli.FunctionalTests.csproj @@ -28,9 +28,6 @@ PreserveNewest - - PreserveNewest - \ No newline at end of file From 12a325b393d6dfd29a1a424a299b00206d4d1d79 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 19:56:07 +0100 Subject: [PATCH 16/24] removed logging --- src/Analyzer.Cli/CommandLineParser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 58e71798..c62a0a8e 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -224,7 +224,6 @@ private int AnalyzeTemplateCommandHandler( } int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); - Console.WriteLine(Environment.NewLine + $"Analyzed the template with {numOfFilesAnalyzed} parameter {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); } FinishAnalysis(); From 4cc57d8203ce86ce3d79425cafcdfd63ea93d38a Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 19:58:23 +0100 Subject: [PATCH 17/24] changed comments --- src/Analyzer.Cli/CommandLineParser.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index c62a0a8e..309a9a1e 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -205,7 +205,6 @@ private int AnalyzeTemplateCommandHandler( } else { - // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input var parametersFiles = FindParameterFileForTemplate(templateFilePath); List exitCodes = new List(); @@ -260,7 +259,6 @@ private int AnalyzeDirectoryCommandHandler( List exitCodes = new List(); foreach (FileInfo file in filesToAnalyze) { - // Check if a parameters.json file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input var parametersFiles = FindParameterFileForTemplate(file); if (parametersFiles.Count() > 0) { @@ -378,6 +376,7 @@ private IEnumerable FindTemplateFilesInDirectory(DirectoryInfo directo return armTemplates.Concat(bicepTemplates); } + // Check if a parameters file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input private IEnumerable FindParameterFileForTemplate(FileInfo template) { var parametersFiles = template.Directory.GetFiles( From 7d27c203717c2237c2c4d73479e6a22c9eeb5b29 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 19:59:31 +0100 Subject: [PATCH 18/24] fixed comment --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 309a9a1e..2405ce8a 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -376,7 +376,7 @@ private IEnumerable FindTemplateFilesInDirectory(DirectoryInfo directo return armTemplates.Concat(bicepTemplates); } - // Check if a parameters file is present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input + // Check if parameters*.json files are present according to naming standards here https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name, and if so use it as the parametersFile input private IEnumerable FindParameterFileForTemplate(FileInfo template) { var parametersFiles = template.Directory.GetFiles( From 728fa061c17462d071e3dfb24a74567eddde0a8b Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:00:41 +0100 Subject: [PATCH 19/24] Update src/Analyzer.Cli/CommandLineParser.cs Co-authored-by: Vera Bogdanich Espina --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 2405ce8a..a424aef9 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -206,7 +206,7 @@ private int AnalyzeTemplateCommandHandler( else { var parametersFiles = FindParameterFileForTemplate(templateFilePath); - List exitCodes = new List(); + var exitCodes = new List(); if (parametersFiles.Count() > 0) { From cab1d575afc02ab40806632ce2f161c229dc617e Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:00:52 +0100 Subject: [PATCH 20/24] Update src/Analyzer.Cli/CommandLineParser.cs Co-authored-by: Vera Bogdanich Espina --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index a424aef9..4457813f 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -256,7 +256,7 @@ private int AnalyzeDirectoryCommandHandler( // Log root directory info to be analyzed Console.WriteLine(Environment.NewLine + Environment.NewLine + $"Directory: {directoryPath}"); - List exitCodes = new List(); + var exitCodes = new List(); foreach (FileInfo file in filesToAnalyze) { var parametersFiles = FindParameterFileForTemplate(file); From e040c73f12817287c5b07aa77d96fc912e180111 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Tue, 1 Nov 2022 20:01:40 +0100 Subject: [PATCH 21/24] forgot to remove line, my bad --- src/Analyzer.Cli/CommandLineParser.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 2405ce8a..a662d9a5 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -221,8 +221,6 @@ private int AnalyzeTemplateCommandHandler( { exitCode = AnalyzeTemplate(templateFilePath, null); } - - int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); } FinishAnalysis(); From 3958efec99877e5ff148b75b90648fef3a0c64b7 Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:51:56 +0100 Subject: [PATCH 22/24] Update src/Analyzer.Cli/CommandLineParser.cs Co-authored-by: Anais Miller --- src/Analyzer.Cli/CommandLineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index 18a8ceaf..a56a95a1 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -274,7 +274,7 @@ private int AnalyzeDirectoryCommandHandler( int numOfFilesAnalyzed = exitCodes.Where(x => x == ExitCode.Success || x == ExitCode.Violation).Count(); Console.WriteLine(Environment.NewLine + $"Analyzed {numOfFilesAnalyzed} {(numOfFilesAnalyzed == 1 ? "file" : "files")}."); - ExitCode exitCode = AnalyzeExitCodes(exitCodes); + var exitCode = AnalyzeExitCodes(exitCodes); FinishAnalysis(); From 840c395448cdc752f07a14c477a689f70ce0eacf Mon Sep 17 00:00:00 2001 From: Pim Simons <32359437+pim-simons@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:52:21 +0100 Subject: [PATCH 23/24] Update README.md Co-authored-by: Anais Miller --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f57f5364..1f3cd7b8 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Argument | Description **(Optional)** `-v` or `--verbose` | Shows details about the analysis **(Optional)** `--include-non-security-rules` | Run all the rules against the templates, including non-security rules - The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA will check if templates with the [general naming standards defined by Microsoft](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/parameter-files#file-name) are present in the folder, otherwise it generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. + The Template BPA runs the [configured rules](#understanding-and-customizing-rules) against the provided template and its corresponding [template parameters](https://docs.microsoft.com/azure/azure-resource-manager/templates/parameter-files), if specified. If no template parameters are specified, then the Template BPA will check if templates with the [general naming standards defined by Microsoft](https://learn.microsoft.com/azure/azure-resource-manager/templates/parameter-files#file-name) are present in the same folder, otherwise it generates the minimum number of placeholder parameters to properly evaluate [template functions](https://docs.microsoft.com/azure/azure-resource-manager/templates/template-functions) in the template. **Note**: Providing the Template BPA with template parameter values will result in more accurate results as it will more accurately represent your deployments. The values provided to parameters may affect the evaluation of the Template BPA rule, altering its results. That said, **DO NOT** save sensitive data (passwords, connection strings, etc.) in parameter files in your repositories. Instead, [retrieve these values from your template from Azure Key Vault](https://docs.microsoft.com/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-static-id). From c62c0d7cd7fcfd0904a120161940a49bdff76322 Mon Sep 17 00:00:00 2001 From: Pim Simons Date: Wed, 2 Nov 2022 08:21:49 +0100 Subject: [PATCH 24/24] change Name to FullName in logging --- .../CommandLineParserTests.cs | 8 ++++---- src/Analyzer.Cli/CommandLineParser.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs index 0dfb7f2a..85294ab6 100644 --- a/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs +++ b/src/Analyzer.Cli.FunctionalTests/CommandLineParserTests.cs @@ -178,8 +178,8 @@ public void AnalyzeDirectory_DirectoryWithInvalidTemplates_LogsExpectedErrorInSa var sarifOutput = JObject.Parse(File.ReadAllText(outputFilePath)); var toolNotifications = sarifOutput["runs"][0]["invocations"][0]["toolExecutionNotifications"]; - Assert.AreEqual("An exception occurred while analyzing template AnInvalidTemplate.json", toolNotifications[0]["message"]["text"]); - Assert.AreEqual("An exception occurred while analyzing template AnInvalidTemplate.bicep", toolNotifications[1]["message"]["text"]); + Assert.AreEqual(toolNotifications[0]["message"]["text"].ToString(), $"An exception occurred while analyzing template {Path.Combine(directoryToAnalyze, "AnInvalidTemplate.json")}"); + Assert.AreEqual(toolNotifications[1]["message"]["text"].ToString(), $"An exception occurred while analyzing template {Path.Combine(directoryToAnalyze, "AnInvalidTemplate.bicep")}"); Assert.AreEqual("error", toolNotifications[0]["level"]); Assert.AreEqual("error", toolNotifications[1]["level"]); @@ -204,8 +204,8 @@ public void AnalyzeDirectory_ExecutionWithErrorAndWarning_PrintsExpectedMessages } var warningMessage = "An exception occurred when processing the template language expressions"; - var errorMessage1 = "An exception occurred while analyzing template ReportsError.json"; - var errorMessage2 = "An exception occurred while analyzing template ReportsError2.json"; + var errorMessage1 = $"An exception occurred while analyzing template {Path.Combine(directoryToAnalyze, "ReportsError.json")}"; + var errorMessage2 = $"An exception occurred while analyzing template {Path.Combine(directoryToAnalyze, "ReportsError2.json")}"; if (!multipleErrors) { diff --git a/src/Analyzer.Cli/CommandLineParser.cs b/src/Analyzer.Cli/CommandLineParser.cs index a56a95a1..26604502 100644 --- a/src/Analyzer.Cli/CommandLineParser.cs +++ b/src/Analyzer.Cli/CommandLineParser.cs @@ -298,11 +298,11 @@ private ExitCode AnalyzeTemplate(FileInfo templateFilePath, FileInfo parametersF { if (parametersFilePath != null) { - logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name} with parameters file {parametersFilePath.Name}"); + logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.FullName} with parameters file {parametersFilePath.FullName}"); } else { - logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.Name}"); + logger.LogError(exception, $"An exception occurred while analyzing template {templateFilePath.FullName}"); } return (exception.Message == TemplateAnalyzer.BicepCompileErrorMessage)