Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion infrastructure/afd-apim/create.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"\n",
"# 2) Service-defined parameters (please do not change these unless you know what you're doing)\n",
"rg_name = utils.get_infra_rg_name(deployment, index)\n",
"rg_tags = utils.build_infrastructure_tags(deployment)\n",
"apim_network_mode = APIMNetworkMode.EXTERNAL_VNET\n",
"\n",
"# 3) Define the APIs and their operations and policies\n",
Expand Down Expand Up @@ -95,7 +96,7 @@
"}\n",
"\n",
"# 2) Run the deployment\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters)\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters, rg_tags = rg_tags)\n",
"\n",
"# 3) Print a deployment summary, if successful; otherwise, exit with an error\n",
"if not output.success:\n",
Expand Down
3 changes: 2 additions & 1 deletion infrastructure/apim-aca/create.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"\n",
"# 2) Service-defined parameters (please do not change these)\n",
"rg_name = utils.get_infra_rg_name(deployment, index)\n",
"rg_tags = utils.build_infrastructure_tags(deployment)\n",
"\n",
"# 3) Define the APIs and their operations and policies\n",
"\n",
Expand Down Expand Up @@ -83,7 +84,7 @@
"}\n",
"\n",
"# 2) Run the deployment\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters)\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters, rg_tags = rg_tags)\n",
"\n",
"# 3) Check the deployment outputs\n",
"if not output.success:\n",
Expand Down
3 changes: 2 additions & 1 deletion infrastructure/simple-apim/create.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"\n",
"# 2) Service-defined parameters (please do not change these)\n",
"rg_name = utils.get_infra_rg_name(deployment, index)\n",
"rg_tags = utils.build_infrastructure_tags(deployment)\n",
"\n",
"# 3) Define the APIs and their operations and policies\n",
"\n",
Expand Down Expand Up @@ -68,7 +69,7 @@
"}\n",
"\n",
"# 2) Run the deployment\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters)\n",
"output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters, rg_tags = rg_tags)\n",
"\n",
"# 3) Check the deployment outputs\n",
"if not output.success:\n",
Expand Down
52 changes: 48 additions & 4 deletions shared/python/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,40 @@
CONSOLE_WIDTH = 175


# ------------------------------
# HELPER FUNCTIONS
# ------------------------------

def build_infrastructure_tags(infrastructure: str | INFRASTRUCTURE, custom_tags: dict | None = None) -> dict:
"""
Build standard tags for infrastructure resource groups, including required 'infrastructure' and infrastructure name tags.

Args:
infrastructure (str | INFRASTRUCTURE): The infrastructure type/name.
custom_tags (dict, optional): Additional custom tags to include.

Returns:
dict: Combined tags dictionary with standard and custom tags.
"""

# Convert infrastructure enum to string value if needed
if hasattr(infrastructure, 'value'):

Copilot AI Jun 9, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Instead of using hasattr to detect an enum, consider using isinstance(infrastructure, INFRASTRUCTURE) for clearer intent and to avoid false positives on objects with a value attribute.

Suggested change
if hasattr(infrastructure, 'value'):
if isinstance(infrastructure, INFRASTRUCTURE):

Copilot uses AI. Check for mistakes.
infra_name = infrastructure.value
else:
infra_name = str(infrastructure)

# Build standard tags
standard_tags = {
'infrastructure': infra_name
}

# Add custom tags if provided
if custom_tags:
standard_tags.update(custom_tags)

return standard_tags


# ------------------------------
# CLASSES
# ------------------------------
Expand Down Expand Up @@ -272,7 +306,7 @@ def get_azure_role_guid(role_name: str) -> Optional[str]:
return None


def create_bicep_deployment_group(rg_name: str, rg_location: str, deployment: str | INFRASTRUCTURE, bicep_parameters: dict, bicep_parameters_file: str = 'params.json') -> Output:
def create_bicep_deployment_group(rg_name: str, rg_location: str, deployment: str | INFRASTRUCTURE, bicep_parameters: dict, bicep_parameters_file: str = 'params.json', rg_tags: dict | None = None) -> Output:
"""
Create a Bicep deployment in a resource group, writing parameters to a file and running the deployment.
Creates the resource group if it does not exist.
Expand All @@ -283,13 +317,14 @@ def create_bicep_deployment_group(rg_name: str, rg_location: str, deployment: st
deployment (str | INFRASTRUCTURE): Deployment name or enum value.
bicep_parameters: Parameters for the Bicep template.
bicep_parameters_file (str, optional): File to write parameters to.
rg_tags (dict, optional): Additional tags to apply to the resource group.

Returns:
Output: The result of the deployment command.
"""

# Create the resource group if doesn't exist
create_resource_group(rg_name, rg_location)
create_resource_group(rg_name, rg_location, rg_tags)

if hasattr(deployment, 'value'):
deployment_name = deployment.value
Expand All @@ -311,13 +346,14 @@ def create_bicep_deployment_group(rg_name: str, rg_location: str, deployment: st
return run(f"az deployment group create --name {deployment_name} --resource-group {rg_name} --template-file main.bicep --parameters {bicep_parameters_file} --query \"properties.outputs\"",
f"Deployment '{deployment_name}' succeeded", f"Deployment '{deployment_name}' failed.")

def create_resource_group(rg_name: str, resource_group_location: str | None = None) -> None:
def create_resource_group(rg_name: str, resource_group_location: str | None = None, tags: dict | None = None) -> None:
"""
Create a resource group in Azure if it does not already exist.

Args:
rg_name (str): Name of the resource group.
resource_group_location (str, optional): Azure region for the resource group.
tags (dict, optional): Additional tags to apply to the resource group.

Returns:
None
Expand All @@ -326,7 +362,15 @@ def create_resource_group(rg_name: str, resource_group_location: str | None = No
if not does_resource_group_exist(rg_name):
print_info(f"Creating the resource group now...")

run(f"az group create --name {rg_name} --location {resource_group_location} --tags source=apim-sample",
# Build the tags string for the Azure CLI command
tag_string = "source=apim-sample"

Copilot AI Jun 9, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Wrap the default source tag value in quotes (e.g. source="apim-sample") for consistency with how custom tag values are quoted, and to prevent issues if the value ever contains special characters.

Suggested change
tag_string = "source=apim-sample"
tag_string = "source=\"apim-sample\""

Copilot uses AI. Check for mistakes.
if tags:
for key, value in tags.items():

Copilot AI Jun 9, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] Iterating tags.items() can produce a non-deterministic order; consider iterating over sorted(tags) to keep the CLI command consistent across runs and easier to test.

Suggested change
for key, value in tags.items():
for key, value in sorted(tags.items()):

Copilot uses AI. Check for mistakes.
# Escape values that contain spaces or special characters
escaped_value = value.replace('"', '\\"') if isinstance(value, str) else str(value)
tag_string += f" {key}=\"{escaped_value}\""
Comment on lines +370 to +371

Copilot AI Jun 9, 2025

Copy link

Choose a reason for hiding this comment

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

Manual string escaping may miss other shell metacharacters; using shlex.quote(value) would more robustly escape any special characters for the shell.

Suggested change
escaped_value = value.replace('"', '\\"') if isinstance(value, str) else str(value)
tag_string += f" {key}=\"{escaped_value}\""
escaped_value = shlex.quote(value) if isinstance(value, str) else shlex.quote(str(value))
tag_string += f" {key}={escaped_value}"

Copilot uses AI. Check for mistakes.

run(f"az group create --name {rg_name} --location {resource_group_location} --tags {tag_string}",
f"Resource group '{rg_name}' created",
f"Failed to create the resource group '{rg_name}'",
False, True, False, False)
Expand Down
Loading