Skip to content

Mark conditional resources as nullable for the purposes of property access #16987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jeskew
Copy link
Member

@jeskew jeskew commented Apr 23, 2025

Resolves #3750

This PR updates .? is compiled when used on top-level properties of resources and modules. The compiler will use the condition of the resource or module as the guard clause in an if expression, substituting null() if the condition is not met. For example, in the following template:

param condition bool

resource r 'RP.Namespace/foos@1999-12-31' = if (condition) {
  ...
}

output prop string = r.?properties.bar

The vaule of the prop output will compile to an expression like [tryGet(if(parameters('condition'), reference('r'), null()), 'bar')].

Type analysis to make sure a warning is emitted when a .? should be used but is not and to make sure chained accesses are properly typed was complected by the fact that references are only situationally nullable. For example, given the statement:

resource accounts 'Microsoft.Storage/storageAccounts@2023-05-01' = [for i in range(0, 10): if (i % 2 == 0) {
  name: 'sa${i}'
}]

then the expression accounts[1].properties (which compiles to [reference('accounts[1]').properties]) will cause a deployment failure, whereas map(accounts, a => a.properties) (which compiles to [map(references('accounts'), lambda('a', lambdaVariables('a').properties))]) will not because the output of references() only includes resources with a true or absent condition. The dependsOn property in ARM is also specifically allowed to refer to resources with a false condition, but it may be a problem if the parent or scope property in Bicep refers to a resource with a false condition.

I opted to make property access on a base that is a resource nullable. E.g., r in the first example above is not nullable, but it becomes nullable when used in the expression r.properties. Similarly for collections, accounts in the second example is an array of nonnullable value, but accounts[<index>] becomes nullable when used in an expression like accounts[<index>].properties. This seems error prone, however, so it's probably best to tag this for a team discussion.

Microsoft Reviewers: Open in CodeFlow

@jeskew jeskew requested a review from Copilot April 23, 2025 23:04
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses issue #3750 by updating tests to correctly mark conditional resources as nullable when accessing their properties, ensuring that property access on conditionally available resources does not lead to runtime failures. Key changes include:

  • Removing explicit resourceId(...) calls in secure output test assertions in favor of string symbolic names.
  • Updating resource reference functions in resource list tests to use resourceId(...) instead of literal strings.
  • Adding non-null assertions to resource property accesses in various test scenarios.

Reviewed Changes

Copilot reviewed 24 out of 38 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Bicep.Core.IntegrationTests/SecureOutputsTests.cs Replaced resourceId(...) with symbolic string names in secure output value assertions.
src/Bicep.Core.IntegrationTests/Scenarios/ResourceListFunctionTests.cs Updated listKeys(...) calls to use resourceId(...) consistently for resource references.
src/Bicep.Core.IntegrationTests/Scenarios/InliningResourcesAndModulesTests.cs Added non-null assertions to resource property accesses.
src/Bicep.Core.IntegrationTests/ScenarioTests.cs Modified outputs to ensure correct resolution when conditional resources are used.
src/Bicep.Core.IntegrationTests/DereferenceTests.cs Inserted non-null operators to guard against potentially null resource properties.
src/Bicep.Core.IntegrationTests/DeployTimeConstantTests.cs Adjusted resource name lookups with non-null assertions for improved safety.
Files not reviewed (14)
  • src/Bicep.Core.IntegrationTests/Files/SymbolicNameTests/ResourceInfo/sql-database-with-management/modules/sql-database.bicep: Language not supported
  • src/Bicep.Core.IntegrationTests/Files/SymbolicNameTests/ResourceInfo/sql-database-with-management/modules/sql-logical-server.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/InvalidModules_LF/main.diagnostics.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/InvalidResources_CRLF/main.diagnostics.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/Loops_LF/main.diagnostics.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/Modules_CRLF/main.json: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/Modules_CRLF/main.sourcemap.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/Outputs_CRLF/main.json: Language not supported
  • src/Bicep.Core.Samples/Files/baselines/Resources_CRLF/main.diagnostics.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/user_submitted/101/azure-bastion/main.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/user_submitted/201/avd-backplane-with-network-and-storage-and-monitoring/avd-image-builder-module.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/user_submitted/201/vm-new-or-existing-conditions/main.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/user_submitted/201/vnet-with-subnet-and-user-defined-route/main.bicep: Language not supported
  • src/Bicep.Core.Samples/Files/user_submitted/301/sql-database-with-management/modules/sql-database.bicep: Language not supported
Comments suppressed due to low confidence (2)

src/Bicep.Core.IntegrationTests/SecureOutputsTests.cs:218

  • Removing the resourceId(...) call in favor of a symbolic name here may lead to a mismatch in resource reference resolution when the deployment engine expects a fully-qualified resource ID. Please confirm that the secure output resolution logic is compatible with this change.
result.Template.Should().HaveValueAtPath("$.resources['key'].properties.value", "[listOutputsWithSecureValues('secureOuputs', '2022-09-01').secureOutput]");

src/Bicep.Core.IntegrationTests/Scenarios/ResourceListFunctionTests.cs:41

  • Changing the literal string to a call to resourceId(...) modifies the input for the listKeys function; please verify that this adjustment aligns with the intended reference resolution behavior and that it properly covers the deployment semantics for resource keys.
result.Template.Should().HaveValueAtPath("$.outputs['pkMethod'].value", "[listKeys(resourceId('Microsoft.Storage/storageAccounts', 'testacc'), '2019-06-01').keys[0].value]");

Copy link
Contributor

Test this change out locally with the following install scripts (Action run 14629761279)

VSCode
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-vsix.sh) --run-id 14629761279
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-vsix.ps1) } -RunId 14629761279"
Azure CLI
  • Mac/Linux
    bash <(curl -Ls https://aka.ms/bicep/nightly-cli.sh) --run-id 14629761279
  • Windows
    iex "& { $(irm https://aka.ms/bicep/nightly-cli.ps1) } -RunId 14629761279"

Copy link
Contributor

Dotnet Test Results

    78 files   -     39      78 suites   - 39   34m 29s ⏱️ - 19m 3s
12 048 tests  -      5  12 048 ✅  -      5  0 💤 ±0  0 ❌ ±0 
27 854 runs   - 13 898  27 854 ✅  - 13 898  0 💤 ±0  0 ❌ ±0 

Results for commit 6b034ef. ± Comparison against base commit 813df1e.

This pull request removes 1795 and adds 618 tests. Note that renamed tests count towards both.

		nestedProp1: 1
		nestedProp2: 2
		prop1: true
		prop2: false
	1
	2
	\$'")
	prop1: true
	prop2: false
…
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
�ӻ
�0\u0014\u0006��>E�\u0003��6V��"��\u0000�=b���F(��n:�K�K/����\u0013���'�޴[0\u0019�
�q\u001c�\u0004��:����\u001d&5b��"���\u0008�v�Nz�\u001bkj��\u001c{� ����\u0005$LK%�\u0007�\u0005Q�H����C5\u0016���s��\u0019�����ny1�|�cz\u0003>�7�\u0015}�?\u0015\u00121E)�S�\u0010��`.��d�����������B���\u0008u��'sm�to��y�t^���V\u0000\u000c\u0000\u0000,"Value cannot be null. (Parameter 'source')")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
��K\u000e� \u0010\u0000P֞�\u0013P\u0006��E�.�\u0002Q\u0012?)5��&ƻ;]���qS[\u0013y$,\u0018\u0008�gX����كk\u0003�e�\u000bF�Ƒ�29>\u0000\u0005\u0004�\u000b'�(�\u0008���$t!�\u0016S�c�\u001f$6��S�*0RI�\u0000�)0\u0002�
c��\u0018Ы�Ǫ�\u0017��<;��\u000f\u000b�\u0005K\u001f4Kz��o]ѧ��\\u0010P��G�\u0000X�\u00008��R��^����\u0019dY�eKx\u0002-�\u0010+\u0000\u000c\u0000\u0000,"The path: index.json was not found in artifact contents")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000
��O\u000b�0\u0014\u0000��\u0014�\u000f0���Z�!�\u0011\u0016\u0004]c���\u0016j �囇�t�O�~��ރ��y#�^��D�IQ\u0012P�w	�\u001b5|���\u0006\u0003��|��J!9B�\u0016��҅)e��~\u0010�c]�Y\u00120	\u0002�\u0003P\u001e\u0011\u001e��`fb�\u0018S���K��qR�ky˛��+}�mZ\u001d>�7�\u0011}���\u00011a\u0006.9e\u000cQ_x�\u001f�(=����t��܆��氋�\u00056?��5Y�eY�{\u0001�7\u001f�\u0000\u000c\u0000\u0000,"'7' is an invalid end of a number. Expected a delimiter. Path: $.INVALID_JSON | LineNumber: 0 | BytePositionInLine: 20.")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0003�ӽ
� \u0010\u0007p�>E����S�!{Ǿ���~\u0010S�!��w�\u0019
\u001d\u0012��I��@\u0007��\u0013��to�]i�e��\u0016\u0014���
\u0002E4z>@�\u000c�\u000b0В\u0014cI?�$#Z�M\u0013FY�\u001f$���sU\u0016�I\u0012�2�e��ڄ�a�����S�ګ�;�/��CC5հ�C�Q����;>�\u001f@0�a\u0017\u001a�\u001a�\u0002C�\u0017�����X{�(��h
OݗF�\u0000\u000c\u0000\u0000,"The path: index.json was not found in artifact contents")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0003�ӽ
�0\u0010\u0007��>E�\u0003�IsI���Ep�\u0001b{b���V(��n;�K�K?\u0004�\u001bs\u0007w��3�o�-�\u0004ˊI��gdl��\u0001z�;\u0002\u000c\u0011�\u0013n����f�Mzܫږ�*s��A���:�0\u0012\u0006\u0014@�!d�\u0008-u�jk�PM����9J�\u0004\u001bv�nyמ
�/�Mg��~�����.�\u0008�y\u0010\u0018\u0011hA�VҴ��%����\u0007�\u000e96\u0005�5&;̎Xz\u001bz��
�K��8��L�\u0005[!K�\u0000\u000c\u0000\u0000,"Value cannot be null. (Parameter 'source')")
Bicep.Core.IntegrationTests.AzTypesViaRegistryTests ‑ Bicep_compiler_handles_corrupted_extension_package_gracefully (\u001f�\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0003��O\u000b�0\u0014\u0000��\u0014�\u000f07��e�!�\u0011\u0016\u0004]c���\u0016j �囇�t�O�~��ރ���Fܽ�׉���$�Jp	�\u001b5$@�~��BL�QD�\u0010\u001c!\��I�GY�´2F�\u001f�ͱ��,	�\u0002\u0001�K���j�<93�sW�����K��qR�ky˛��+}�cZ\u001d>�7\�o�O=@L��S�0D�����(3����t��܆��氋�\u00056?��=Y�eY�{\u0001=��\u0000\u000c\u0000\u0000,"'7' is an invalid end of a number. Expected a delimiter. Path: $.INVALID_JSON | LineNumber: 0 | BytePositionInLine: 20.")
Bicep.Core.IntegrationTests.DereferenceTests ‑ Possibly_disabled_resources_should_raise_no_diagnostics_when_used_as_parent_or_dependency
Bicep.Core.IntegrationTests.DereferenceTests ‑ References_to_syntactic_ancestor_in_nested_resources_should_allow_regular_dereference_without_diagnostics
Bicep.Core.IntegrationTests.DereferenceTests ‑ Safe_dereference_guards_against_dereferencing_properties_from_disabled_child_resources
Bicep.Core.IntegrationTests.DereferenceTests ‑ Safe_dereference_guards_against_dereferencing_properties_from_disabled_child_resources_with_resourceInfo
…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal - Better validation for conditional resources/modules
1 participant