diff --git a/src/Bicep.Core.Samples/Files/baselines/Resources_CRLF/main.diagnostics.bicep b/src/Bicep.Core.Samples/Files/baselines/Resources_CRLF/main.diagnostics.bicep index 9cba1ca1596..f8ea8cff839 100644 --- a/src/Bicep.Core.Samples/Files/baselines/Resources_CRLF/main.diagnostics.bicep +++ b/src/Bicep.Core.Samples/Files/baselines/Resources_CRLF/main.diagnostics.bicep @@ -140,6 +140,7 @@ output siteApiVersion string = site.apiVersion output siteType string = site.type resource nested 'Microsoft.Resources/deployments@2019-10-01' = { +//@[16:60) [no-deployments-resources (Warning)] Resource 'nested' of type 'Microsoft.Resources/deployments@2019-10-01' should instead be declared as a Bicep module. (CodeDescription: bicep core(https://aka.ms/bicep/linter/no-deployments-resources)) |'Microsoft.Resources/deployments@2019-10-01'| name: 'nestedTemplate1' properties: { mode: 'Incremental' diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/NoDeploymentsResourcesRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/NoDeploymentsResourcesRuleTests.cs new file mode 100644 index 00000000000..8758c00033e --- /dev/null +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/NoDeploymentsResourcesRuleTests.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Bicep.Core.Analyzers.Linter.Rules; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests; + +[TestClass] +public class NoDeploymentsResourcesRuleTests : LinterRuleTestsBase +{ + private void CompileAndTest(string text, int expectedDiagnosticCount, Options? options = null) + { + AssertLinterRuleDiagnostics(NoDeploymentsResourcesRule.Code, text, expectedDiagnosticCount, options); + } + + [DataRow(""" +param name string +param specId string +resource foo 'Microsoft.Resources/deployments@2021-04-01' = { + name: name + properties: { + mode: 'Incremental' + templateLink: { + uri: specId + } + parameters: {} + } +} +""")] + [DataRow(""" +param name string +resource foo 'Microsoft.Resources/deployments@2021-04-01' existing = { + name: name +} +""")] + [DataTestMethod] + public void Linter_validation_should_warn_for_nested_deployment_resources(string text) + { + CompileAndTest(text, 1); + } + + [DataRow(""" +param name string +param location string +resource foo 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + kind: 'AzureCLI' + name: name + properties: { + azCliVersion: 's' + retentionInterval: 's' + } + location: location +} +""")] + [DataTestMethod] + public void Linter_validation_should_not_warn_for_non_deployment_resource_types(string text) + { + CompileAndTest(text, 0); + } +} diff --git a/src/Bicep.Core/Analyzers/Linter/Rules/NoDeploymentsResourcesRule.cs b/src/Bicep.Core/Analyzers/Linter/Rules/NoDeploymentsResourcesRule.cs new file mode 100644 index 00000000000..15aff6a81d2 --- /dev/null +++ b/src/Bicep.Core/Analyzers/Linter/Rules/NoDeploymentsResourcesRule.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bicep.Core.Diagnostics; +using Bicep.Core.Semantics; +using Bicep.Core.Semantics.Metadata; +using Bicep.Core.Syntax; +using Bicep.Core.TypeSystem.Az; + +namespace Bicep.Core.Analyzers.Linter.Rules; + +public sealed class NoDeploymentsResourcesRule : LinterRuleBase +{ + public new const string Code = "no-deployments-resources"; + + public NoDeploymentsResourcesRule() : base( + code: Code, + description: CoreResources.NoDeploymentsResourcesRuleDescription, + docUri: new Uri($"https://aka.ms/bicep/linter/{Code}")) + { + } + + public override string FormatMessage(params object[] values) + => string.Format(CoreResources.NoDeploymentsResourcesRuleMessageFormat, values); + + public override IEnumerable AnalyzeInternal(SemanticModel model, DiagnosticLevel diagnosticLevel) + { + foreach (var resource in model.DeclaredResources.Where(r => r.IsAzResource)) + { + if (!LanguageConstants.ResourceTypeComparer.Equals(resource.TypeReference.FormatType(), AzResourceTypeProvider.ResourceTypeDeployments)) + { + continue; + } + + var typeSyntax = resource.Symbol.DeclaringResource.Type; + yield return CreateDiagnosticForSpan(diagnosticLevel, typeSyntax.Span, resource.Symbol.Name, resource.TypeReference.FormatName()); + } + } +} \ No newline at end of file diff --git a/src/Bicep.Core/CoreResources.Designer.cs b/src/Bicep.Core/CoreResources.Designer.cs index d4172601682..3b32bf4b9f0 100644 --- a/src/Bicep.Core/CoreResources.Designer.cs +++ b/src/Bicep.Core/CoreResources.Designer.cs @@ -416,6 +416,24 @@ internal class CoreResources { } } + /// + /// Looks up a localized string similar to Bicep modules are recommended instead of representing nested or linked deployments as a resource.. + /// + internal static string NoDeploymentsResourcesRuleDescription { + get { + return ResourceManager.GetString("NoDeploymentsResourcesRuleDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Resource "{0}" of type "{1}" should instead be declared as a Bicep module.. + /// + internal static string NoDeploymentsResourcesRuleMessageFormat { + get { + return ResourceManager.GetString("NoDeploymentsResourcesRuleMessageFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to A resource location should not use a hard-coded string or variable value. Change variable '{0}' into a parameter instead.. /// diff --git a/src/Bicep.Core/CoreResources.resx b/src/Bicep.Core/CoreResources.resx index 1e4151729d5..1fb3562e625 100644 --- a/src/Bicep.Core/CoreResources.resx +++ b/src/Bicep.Core/CoreResources.resx @@ -446,6 +446,12 @@ The "{0}" metadata property conflicts with the "{1}" decorator and will be overwritten. + + Bicep modules are recommended instead of representing nested or linked deployments as a resource. + + + Resource '{0}' of type '{1}' should instead be declared as a Bicep module. + Asserts diff --git a/src/vscode-bicep/schemas/bicepconfig.schema.json b/src/vscode-bicep/schemas/bicepconfig.schema.json index c6da1b13ce2..11579cfcfe2 100644 --- a/src/vscode-bicep/schemas/bicepconfig.schema.json +++ b/src/vscode-bicep/schemas/bicepconfig.schema.json @@ -315,6 +315,16 @@ } ] }, + "no-deployments-resources": { + "allOf": [ + { + "description": "Bicep modules are recommended instead of representing nested or linked deployments as a resource. See https://aka.ms/bicep/linter/no-deployments-resources" + }, + { + "$ref": "#/definitions/rule-def-level-warning" + } + ] + }, "no-hardcoded-env-urls": { "allOf": [ {