Skip to content

Commit 0a2bff4

Browse files
authored
[Resource] Support 'using none' in .bicepparam files for deployment commands
1 parent 5352ca8 commit 0a2bff4

File tree

5 files changed

+181
-7
lines changed

5 files changed

+181
-7
lines changed

src/azure-cli/azure/cli/command_modules/resource/_bicep.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,22 @@ def is_bicepparam_file(file_path):
177177
return file_path.lower().endswith(".bicepparam")
178178

179179

180+
def is_using_none_bicepparam_file(file_path):
181+
"""Check if a .bicepparam file uses 'using none' declaration."""
182+
try:
183+
with open(file_path, 'r') as f:
184+
content = f.read()
185+
except IOError:
186+
return False
187+
for line in content.splitlines():
188+
stripped = line.strip()
189+
if stripped == '' or stripped.startswith('//'):
190+
continue
191+
# The 'using' declaration must be the first non-comment, non-empty statement
192+
return stripped.lower() == 'using none'
193+
return False
194+
195+
180196
def get_bicep_available_release_tags():
181197
try:
182198
os.environ.setdefault("CURL_CA_BUNDLE", certifi.where())

src/azure-cli/azure/cli/command_modules/resource/custom.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
run_bicep_command,
4949
is_bicep_file,
5050
is_bicepparam_file,
51+
is_using_none_bicepparam_file,
5152
ensure_bicep_installation,
5253
remove_bicep_installation,
5354
get_bicep_latest_release_tag,
@@ -977,13 +978,33 @@ def _prepare_deployment_properties_unmodified(cmd, deployment_scope, template_fi
977978
if len(parameters) > 1:
978979
raise ArgumentUsageError("Can not use --parameters argument more than once when using a .bicepparam file")
979980
bicepparam_file = parameters[0][0]
980-
if not is_bicep_file(template_file):
981-
raise ArgumentUsageError("Only a .bicep template is allowed with a .bicepparam parameter file")
982981

983-
build_bicepparam_output = run_bicep_command(cmd.cli_ctx, ["build-params", bicepparam_file, "--bicep-file", template_file, "--stdout"])
984-
build_bicepparam_output_json = json.loads(build_bicepparam_output)
985-
template_content = build_bicepparam_output_json["templateJson"]
986-
bicepparam_json_content = build_bicepparam_output_json["parametersJson"]
982+
if is_using_none_bicepparam_file(bicepparam_file):
983+
# 'using none' bicepparam: template must be provided separately via --template-file
984+
if not template_file:
985+
raise ArgumentUsageError(
986+
"The .bicepparam file uses 'using none', so a --template-file (-f) must be provided.")
987+
988+
# Build params only (no --bicep-file since there's no using declaration to verify)
989+
build_bicepparam_output = run_bicep_command(cmd.cli_ctx, ["build-params", bicepparam_file, "--stdout"])
990+
build_bicepparam_output_json = json.loads(build_bicepparam_output)
991+
bicepparam_json_content = build_bicepparam_output_json["parametersJson"]
992+
993+
# Build template separately
994+
template_content = (
995+
run_bicep_command(cmd.cli_ctx, ["build", "--stdout", template_file])
996+
if is_bicep_file(template_file)
997+
else read_file_content(template_file)
998+
)
999+
else:
1000+
# Normal 'using' declaration: existing behavior
1001+
if not is_bicep_file(template_file):
1002+
raise ArgumentUsageError("Only a .bicep template is allowed with a .bicepparam parameter file")
1003+
1004+
build_bicepparam_output = run_bicep_command(cmd.cli_ctx, ["build-params", bicepparam_file, "--bicep-file", template_file, "--stdout"])
1005+
build_bicepparam_output_json = json.loads(build_bicepparam_output)
1006+
template_content = build_bicepparam_output_json["templateJson"]
1007+
bicepparam_json_content = build_bicepparam_output_json["parametersJson"]
9871008
else:
9881009
template_content = (
9891010
run_bicep_command(cmd.cli_ctx, ["build", "--stdout", template_file])
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using none
2+
3+
param location = 'westus2'
4+
5+
param kind = 'StorageV2'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// This is a comment
2+
// Another comment
3+
4+
using none
5+
6+
param location = 'westus2'
7+
8+
param kind = 'StorageV2'

src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4235,7 +4235,131 @@ def test_resource_deployment_with_bicepparam_and_other_parameter_sources(self):
42354235

42364236
with self.assertRaisesRegex(CLIError, "Can"):
42374237
self.cmd('deployment group create --resource-group {rg} --template-file "{tf}" --parameters {params1} --parameters {params2}')
4238-
4238+
4239+
def test_is_using_none_bicepparam_file(self):
4240+
from azure.cli.command_modules.resource._bicep import is_using_none_bicepparam_file
4241+
import tempfile
4242+
4243+
def _write_temp(content):
4244+
f = tempfile.NamedTemporaryFile(mode='w', suffix='.bicepparam', delete=False)
4245+
f.write(content)
4246+
f.close()
4247+
return f.name
4248+
4249+
# Basic 'using none'
4250+
path = _write_temp("using none\nparam location = 'westus2'\n")
4251+
self.assertTrue(is_using_none_bicepparam_file(path))
4252+
os.unlink(path)
4253+
4254+
# With leading comments
4255+
path = _write_temp("// comment\n// another\nusing none\nparam location = 'westus2'\n")
4256+
self.assertTrue(is_using_none_bicepparam_file(path))
4257+
os.unlink(path)
4258+
4259+
# With leading blank lines
4260+
path = _write_temp("\n\n \nusing none\nparam location = 'westus2'\n")
4261+
self.assertTrue(is_using_none_bicepparam_file(path))
4262+
os.unlink(path)
4263+
4264+
# Case insensitive
4265+
path = _write_temp("Using None\nparam location = 'westus2'\n")
4266+
self.assertTrue(is_using_none_bicepparam_file(path))
4267+
os.unlink(path)
4268+
4269+
path = _write_temp("USING NONE\nparam location = 'westus2'\n")
4270+
self.assertTrue(is_using_none_bicepparam_file(path))
4271+
os.unlink(path)
4272+
4273+
# Normal using declaration
4274+
path = _write_temp("using './main.bicep'\nparam location = 'westus2'\n")
4275+
self.assertFalse(is_using_none_bicepparam_file(path))
4276+
os.unlink(path)
4277+
4278+
# Using with another path
4279+
path = _write_temp("using 'other.bicep'\nparam location = 'westus2'\n")
4280+
self.assertFalse(is_using_none_bicepparam_file(path))
4281+
os.unlink(path)
4282+
4283+
# Non-existent file
4284+
self.assertFalse(is_using_none_bicepparam_file('/nonexistent/path.bicepparam'))
4285+
4286+
@ResourceGroupPreparer(name_prefix='cli_test_deployment_with_bicepparam_using_none')
4287+
def test_resource_group_level_deployment_with_bicepparam_using_none(self):
4288+
curr_dir = os.path.dirname(os.path.realpath(__file__))
4289+
self.kwargs.update({
4290+
'tf': os.path.join(curr_dir, 'data\\bicepparam\\storage_account_template.bicep').replace('\\', '\\\\'),
4291+
'params': os.path.join(curr_dir, 'data\\bicepparam\\using_none_params.bicepparam').replace('\\', '\\\\')
4292+
})
4293+
4294+
self.cmd('deployment group validate --resource-group {rg} --template-file "{tf}" --parameters {params}', checks=[
4295+
self.check('properties.provisioningState', 'Succeeded')
4296+
])
4297+
4298+
self.cmd('deployment group what-if --resource-group {rg} --template-file "{tf}" --parameters {params} --no-pretty-print', checks=[
4299+
self.check('status', 'Succeeded'),
4300+
])
4301+
4302+
self.cmd('deployment group create --resource-group {rg} --template-file "{tf}" --parameters {params}', checks=[
4303+
self.check('properties.provisioningState', 'Succeeded')
4304+
])
4305+
4306+
def test_resource_deployment_with_bicepparam_using_none_and_no_template(self):
4307+
import tempfile
4308+
4309+
f = tempfile.NamedTemporaryFile(mode='w', suffix='.bicepparam', delete=False)
4310+
f.write("using none\nparam location = 'westus2'\n")
4311+
f.close()
4312+
4313+
self.kwargs.update({
4314+
'rg': "exampleGroup",
4315+
'params': f.name
4316+
})
4317+
4318+
# The CLI argument validator requires --template-file, --template-uri, or --template-spec,
4319+
# so omitting all of them fails before reaching the bicepparam logic.
4320+
with self.assertRaisesRegex(CLIError, "Chose only one of"):
4321+
self.cmd('deployment group create --resource-group {rg} --parameters {params}')
4322+
4323+
os.unlink(f.name)
4324+
4325+
def test_resource_deployment_with_bicepparam_using_none_and_multiple_params(self):
4326+
import tempfile
4327+
4328+
f = tempfile.NamedTemporaryFile(mode='w', suffix='.bicepparam', delete=False)
4329+
f.write("using none\nparam location = 'westus2'\n")
4330+
f.close()
4331+
4332+
self.kwargs.update({
4333+
'rg': "exampleGroup",
4334+
'tf': "./main.bicep",
4335+
'params1': f.name,
4336+
'params2': "./other.json",
4337+
})
4338+
4339+
with self.assertRaisesRegex(CLIError, "Can not use --parameters argument more than once when using a .bicepparam file"):
4340+
self.cmd('deployment group create --resource-group {rg} --template-file "{tf}" --parameters {params1} --parameters {params2}')
4341+
4342+
os.unlink(f.name)
4343+
4344+
def test_resource_deployment_with_bicepparam_and_json_template_still_fails_without_using_none(self):
4345+
"""Ensure existing behavior: non-using-none bicepparam + .json template still errors."""
4346+
import tempfile
4347+
4348+
f = tempfile.NamedTemporaryFile(mode='w', suffix='.bicepparam', delete=False)
4349+
f.write("using './something.bicep'\nparam location = 'westus2'\n")
4350+
f.close()
4351+
4352+
self.kwargs.update({
4353+
'rg': "exampleGroup",
4354+
'tf': "./main.json",
4355+
'params': f.name
4356+
})
4357+
4358+
with self.assertRaisesRegex(CLIError, "Only a .bicep template is allowed with a .bicepparam parameter file"):
4359+
self.cmd('deployment group create --resource-group {rg} --template-file "{tf}" --parameters {params}')
4360+
4361+
os.unlink(f.name)
4362+
42394363
def test_subscription_level_deployment_with_bicep(self):
42404364
curr_dir = os.path.dirname(os.path.realpath(__file__))
42414365
self.kwargs.update({

0 commit comments

Comments
 (0)