diff --git a/sdk/provisioning/Azure.Provisioning/src/BicepDictionaryOfT.cs b/sdk/provisioning/Azure.Provisioning/src/BicepDictionaryOfT.cs index 50ffc87c725a..80f9e441d726 100644 --- a/sdk/provisioning/Azure.Provisioning/src/BicepDictionaryOfT.cs +++ b/sdk/provisioning/Azure.Provisioning/src/BicepDictionaryOfT.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -75,8 +76,26 @@ public static implicit operator BicepDictionary(ProvisioningVariable referenc /// The value. public BicepValue this[string key] { - get => _values[key]; - set => _values[key] = value; + get + { + if (_isOutput) + { + return BicepSyntax.Index(_self!.GetReference(), BicepSyntax.Value(key)); + } + else + { + return _values[key]; + } + } + + set + { + if (_kind == BicepValueKind.Expression || _isOutput) + { + throw new InvalidOperationException($"Cannot assign to {_self?.PropertyName}"); + } + _values[key] = value; + } } public void Add(string key, BicepValue value) => _values.Add(key, value); diff --git a/sdk/provisioning/Azure.Provisioning/src/BicepListOfT.cs b/sdk/provisioning/Azure.Provisioning/src/BicepListOfT.cs index 0a177a379276..c8a018e6cfc6 100644 --- a/sdk/provisioning/Azure.Provisioning/src/BicepListOfT.cs +++ b/sdk/provisioning/Azure.Provisioning/src/BicepListOfT.cs @@ -89,6 +89,10 @@ public BicepValue this[int index] // can reference their members. return _referenceFactory!(BicepSyntax.Index(_expression!, BicepSyntax.Value(index))); } + else if (_isOutput) + { + return BicepSyntax.Index(_self!.GetReference(), BicepSyntax.Value(index)); + } else { return _values[index]; diff --git a/sdk/provisioning/Azure.Provisioning/tests/BicepValues/BicepValueTests.cs b/sdk/provisioning/Azure.Provisioning/tests/BicepValues/BicepValueTests.cs index 820391bd6fe2..3a6e90625bc1 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/BicepValues/BicepValueTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/BicepValues/BicepValueTests.cs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; +using System.Collections.Generic; +using Azure.Provisioning.Primitives; using NUnit.Framework; namespace Azure.Provisioning.Tests.BicepValues; @@ -39,10 +42,136 @@ public void ValidateLiteralBicepValue() AssertExpression("-2147483647", new BicepValue(-2147483647d)); AssertExpression("-2147483648", new BicepValue(-2147483648d)); AssertExpression("json('-2147483649')", new BicepValue(-2147483649d)); + } - static void AssertExpression(string expected, BicepValue bicepValue) + [Test] + public void ValidateOutputArray() + { + var resource = new TestConstruct("test"); + var expression = resource.Properties.Ports[0]; + AssertExpression( + "test.properties.ports[0]", + expression + ); + } + + [Test] + public void ValidateOutputDictionary() + { + var resource = new TestConstruct("test") + { + Properties = new() + }; + var expression = resource.Properties.Endpoints["reference"]; + AssertExpression( + "test.properties.endpoints['reference']", + expression + ); + } + + [Test] + public void ValidateInputArray() + { + var resource = new TestConstruct("test") + { + Properties = new() + { + IpAddresses = ["192.168.1.1"] + } + }; + var expression = resource.Properties.IpAddresses[0]; + AssertExpression( + "'192.168.1.1'", + expression + ); + Assert.Throws(() => { - Assert.AreEqual(expected, bicepValue.ToString()); + var e = resource.Properties.IpAddresses[1]; + }); + } + + [Test] + public void ValidateInputDictionary() + { + var resource = new TestConstruct("test") + { + Properties = new() + { + Tags = new() + { + ["foo"] = "bar" + } + } + }; + var expression = resource.Properties.Tags["foo"]; + AssertExpression( + "'bar'", + expression + ); + Assert.Throws(() => + { + var t = resource.Properties.Tags["bar"]; + }); + } + + private class TestConstruct : ProvisionableResource + { + protected override void DefineProvisionableProperties() + { + _properties = DefineModelProperty("Properties", ["properties"]); } + + public TestProperties Properties + { + get { Initialize(); return _properties!; } + set { Initialize(); AssignOrReplace(ref _properties, value); } + } + private TestProperties? _properties; + + public TestConstruct(string bicepIdentifier, string? resourceVersion = null) : base(bicepIdentifier, "Microsoft.Tests/testResource", resourceVersion ?? "2025-05-27") + { + } + } + + private class TestProperties : ProvisionableConstruct + { + protected override void DefineProvisionableProperties() + { + _ipAddresses = DefineListProperty("IpAddresses", ["ipAddresses"]); + _tags = DefineDictionaryProperty("Tags", ["tags"]); + _ports = DefineListProperty("Ports", ["ports"], isOutput: true); + _endpoints = DefineDictionaryProperty("Endpoints", ["endpoints"], isOutput: true); + } + + public BicepList IpAddresses + { + get { Initialize(); return _ipAddresses!; } + set { Initialize(); AssignOrReplace(ref _ipAddresses, value); } + } + private BicepList? _ipAddresses; + + public BicepDictionary Tags + { + get { Initialize(); return _tags!; } + set { Initialize(); AssignOrReplace(ref _tags, value); } + } + private BicepDictionary? _tags; + + public BicepList Ports + { + get { Initialize(); return _ports!; } + } + private BicepList? _ports; + + public BicepDictionary Endpoints + { + get { Initialize(); return _endpoints!; } + } + private BicepDictionary? _endpoints; + } + + private static void AssertExpression(string expected, BicepValue bicepValue) + { + Assert.AreEqual(expected, bicepValue.ToString()); } }