Skip to content

Commit 460eaaf

Browse files
Merge branch 'main' into feature/get-api-id
2 parents a74d8da + 179ea69 commit 460eaaf

File tree

20 files changed

+1776
-603
lines changed

20 files changed

+1776
-603
lines changed
File renamed without changes.

infrastructure/afd-apim-pe/create.ipynb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,20 @@
2222
"import utils\n",
2323
"from apimtypes import *\n",
2424
"\n",
25-
"# User-defined parameters (change these as needed)\n",
25+
"# ------------------------------\n",
26+
"# USER CONFIGURATION\n",
27+
"# ------------------------------\n",
28+
"\n",
2629
"rg_location = 'eastus2' # Azure region for deployment\n",
2730
"index = 1 # Infrastructure index (use different numbers for multiple environments)\n",
2831
"apim_sku = APIM_SKU.STANDARDV2 # Options: 'STANDARDV2', 'PREMIUMV2' (Basic not supported for private endpoints)\n",
29-
"use_aca = True # Include Azure Container Apps backends\n",
3032
"\n",
31-
"# Create an instance of the desired infrastructure\n",
33+
"\n",
34+
"\n",
35+
"# ------------------------------\n",
36+
"# SYSTEM CONFIGURATION\n",
37+
"# ------------------------------\n",
38+
"\n",
3239
"inb_helper = utils.InfrastructureNotebookHelper(rg_location, INFRASTRUCTURE.AFD_APIM_PE, index, apim_sku) \n",
3340
"success = inb_helper.create_infrastructure()\n",
3441
"\n",
Lines changed: 47 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,13 @@
11
"""
2-
Infrastructure creation module for AFD-APIM-PE.
3-
4-
This module provides a reusable way to create Azure Front Door with API Management
5-
(Private Endpoint) infrastructure that can be called from notebooks or other scripts.
2+
This module provides a reusable way to create Azure Front Door with API Management (Private Endpoint) infrastructure that can be called from notebooks or other scripts.
63
"""
74

85
import sys
9-
import os
106
import argparse
11-
from pathlib import Path
7+
from apimtypes import APIM_SKU, API, GET_APIOperation, BACKEND_XML_POLICY_PATH
8+
from infrastructures import AfdApimAcaInfrastructure
129
import utils
13-
from apimtypes import *
14-
import json
1510

16-
def _create_afd_apim_pe_infrastructure(
17-
rg_location: str = 'eastus2',
18-
index: int | None = None,
19-
apim_sku: APIM_SKU = APIM_SKU.STANDARDV2,
20-
use_aca: bool = True,
21-
custom_apis: list[API] | None = None,
22-
custom_policy_fragments: list[PolicyFragment] | None = None
23-
) -> utils.Output:
24-
"""
25-
Create AFD-APIM-PE infrastructure with the specified parameters.
26-
27-
Args:
28-
rg_location (str): Azure region for deployment. Defaults to 'eastus2'.
29-
index (int | None): Index for the infrastructure. Defaults to None (no index).
30-
apim_sku (APIM_SKU): SKU for API Management. Defaults to STANDARDV2.
31-
use_aca (bool): Whether to include Azure Container Apps. Defaults to True.
32-
custom_apis (list[API] | None): Custom APIs to deploy. If None, uses default Hello World API.
33-
custom_policy_fragments (list[PolicyFragment] | None): Custom policy fragments. If None, uses defaults.
34-
35-
Returns:
36-
utils.Output: The deployment result.
37-
"""
38-
39-
# 1) Setup deployment parameters
40-
deployment = INFRASTRUCTURE.AFD_APIM_PE
41-
rg_name = utils.get_infra_rg_name(deployment, index)
42-
rg_tags = utils.build_infrastructure_tags(deployment)
43-
apim_network_mode = APIMNetworkMode.EXTERNAL_VNET
4411

4512
print(f'\n🚀 Creating AFD-APIM-PE infrastructure...\n')
4613
print(f' Infrastructure : {deployment.value}')
@@ -108,194 +75,72 @@ def _create_afd_apim_pe_infrastructure(
10875
infra_dir = Path(__file__).parent
10976

11077
try:
111-
os.chdir(infra_dir)
112-
print(f'📁 Changed working directory to: {infra_dir}')
113-
114-
# 6) Create the resource group if it doesn't exist
115-
utils.create_resource_group(rg_name, rg_location, rg_tags)
78+
# Create custom APIs for AFD-APIM-PE with optional Container Apps backends
79+
custom_apis = _create_afd_specific_apis(not no_aca)
11680

117-
# 7) First deployment with public access enabled
118-
print('\n🚀 Phase 1: Creating infrastructure with public access enabled...')
119-
output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters)
81+
infra = AfdApimAcaInfrastructure(location, index, apim_sku, infra_apis = custom_apis)
82+
result = infra.deploy_infrastructure()
12083

121-
if not output.success:
122-
print('❌ Phase 1 deployment failed!')
123-
return output
124-
125-
# Extract service details for private link approval
126-
if output.json_data:
127-
apim_service_id = output.get('apimServiceId', 'APIM Service Id', suppress_logging = True)
128-
129-
print('✅ Phase 1 deployment completed successfully!')
130-
131-
# 8) Approve private link connections
132-
print('\n🔗 Approving Front Door private link connections...')
133-
_approve_private_link_connections(apim_service_id)
134-
135-
# 9) Second deployment to disable public access
136-
print('\n🔒 Phase 2: Disabling APIM public access...')
137-
bicep_parameters['apimPublicAccess']['value'] = False
84+
sys.exit(0 if result.success else 1)
13885

139-
output = utils.create_bicep_deployment_group(rg_name, rg_location, deployment, bicep_parameters)
140-
141-
if output.success:
142-
print('\n✅ Infrastructure creation completed successfully!')
143-
if output.json_data:
144-
apim_gateway_url = output.get('apimResourceGatewayURL', 'APIM API Gateway URL', suppress_logging = True)
145-
afd_endpoint_url = output.get('fdeSecureUrl', 'Front Door Endpoint URL', suppress_logging = True)
146-
apim_apis = output.getJson('apiOutputs', 'APIs', suppress_logging = True)
147-
148-
print(f'\n📋 Infrastructure Details:')
149-
print(f' Resource Group : {rg_name}')
150-
print(f' Location : {rg_location}')
151-
print(f' APIM SKU : {apim_sku.value}')
152-
print(f' Use ACA : {use_aca}')
153-
print(f' Gateway URL : {apim_gateway_url}')
154-
print(f' Front Door URL : {afd_endpoint_url}')
155-
print(f' APIs Created : {len(apim_apis)}')
156-
157-
# Perform basic verification
158-
_verify_infrastructure(rg_name, use_aca)
159-
else:
160-
print('❌ Phase 2 deployment failed!')
161-
162-
return output
163-
164-
finally:
165-
# Always restore the original working directory
166-
os.chdir(original_cwd)
167-
print(f'📁 Restored working directory to: {original_cwd}')
168-
169-
def _approve_private_link_connections(apim_service_id: str) -> None:
170-
"""
171-
Approve pending private link connections for the APIM service.
172-
173-
Args:
174-
apim_service_id (str): The resource ID of the APIM service.
175-
"""
176-
177-
# Get all pending private endpoint connections as JSON
178-
output = utils.run(f"az network private-endpoint-connection list --id {apim_service_id} --query \"[?contains(properties.privateLinkServiceConnectionState.status, 'Pending')]\" -o json", print_command_to_run = False)
179-
180-
# Handle both a single object and a list of objects
181-
pending_connections = output.json_data if output.success and output.is_json else []
182-
183-
if isinstance(pending_connections, dict):
184-
pending_connections = [pending_connections]
185-
186-
total = len(pending_connections)
187-
print(f'Found {total} pending private link service connection(s).')
188-
189-
if total > 0:
190-
for i, conn in enumerate(pending_connections, 1):
191-
conn_id = conn.get('id')
192-
conn_name = conn.get('name', '<unknown>')
193-
print(f' {i}/{total}: Approving {conn_name}')
86+
except Exception as e:
87+
print(f'\n💥 Error: {str(e)}')
88+
sys.exit(1)
19489

195-
approve_result = utils.run(
196-
f"az network private-endpoint-connection approve --id {conn_id} --description 'Approved'",
197-
f'Private Link Connection approved: {conn_name}',
198-
f'Failed to approve Private Link Connection: {conn_name}',
199-
print_command_to_run = False
200-
)
20190

202-
print('✅ Private link approvals completed')
203-
else:
204-
print('No pending private link service connections found. Nothing to approve.')
205-
206-
def _verify_infrastructure(rg_name: str, use_aca: bool) -> bool:
91+
def _create_afd_specific_apis(use_aca: bool = True) -> list[API]:
20792
"""
208-
Verify that the infrastructure was created successfully.
93+
Create AFD-APIM-PE specific APIs with optional Container Apps backends.
20994
21095
Args:
211-
rg_name (str): Resource group name.
212-
use_aca (bool): Whether Container Apps were included.
96+
use_aca (bool): Whether to include Azure Container Apps backends. Defaults to true.
21397
21498
Returns:
215-
bool: True if verification passed, False otherwise.
99+
list[API]: List of AFD-specific APIs.
216100
"""
217101

218-
print('\n🔍 Verifying infrastructure...')
102+
# If Container Apps is enabled, create the ACA APIs in APIM
103+
if use_aca:
104+
pol_backend = utils.read_policy_xml(BACKEND_XML_POLICY_PATH)
105+
pol_aca_backend_1 = pol_backend.format(backend_id = 'aca-backend-1')
106+
pol_aca_backend_2 = pol_backend.format(backend_id = 'aca-backend-2')
107+
pol_aca_backend_pool = pol_backend.format(backend_id = 'aca-backend-pool')
108+
109+
# API 1: Hello World (ACA Backend 1)
110+
api_hwaca_1_get = GET_APIOperation('This is a GET for Hello World on ACA Backend 1')
111+
api_hwaca_1 = API('hello-world-aca-1', 'Hello World (ACA 1)', '/aca-1', 'This is the ACA API for Backend 1', pol_aca_backend_1, [api_hwaca_1_get])
112+
113+
# API 2: Hello World (ACA Backend 2)
114+
api_hwaca_2_get = GET_APIOperation('This is a GET for Hello World on ACA Backend 2')
115+
api_hwaca_2 = API('hello-world-aca-2', 'Hello World (ACA 2)', '/aca-2', 'This is the ACA API for Backend 2', pol_aca_backend_2, [api_hwaca_2_get])
116+
117+
# API 3: Hello World (ACA Backend Pool)
118+
api_hwaca_pool_get = GET_APIOperation('This is a GET for Hello World on ACA Backend Pool')
119+
api_hwaca_pool = API('hello-world-aca-pool', 'Hello World (ACA Pool)', '/aca-pool', 'This is the ACA API for Backend Pool', pol_aca_backend_pool, [api_hwaca_pool_get])
120+
121+
return [api_hwaca_1, api_hwaca_2, api_hwaca_pool]
219122

220-
try:
221-
# Check if the resource group exists
222-
if not utils.does_resource_group_exist(rg_name):
223-
print('❌ Resource group does not exist!')
224-
return False
225-
226-
print('✅ Resource group verified')
227-
228-
# Get APIM service details
229-
output = utils.run(f'az apim list -g {rg_name} --query "[0]" -o json', print_command_to_run = False, print_errors = False)
230-
231-
if output.success and output.json_data:
232-
apim_name = output.json_data.get('name')
233-
print(f'✅ APIM Service verified: {apim_name}')
234-
235-
# Check Front Door
236-
afd_output = utils.run(f'az afd profile list -g {rg_name} --query "[0]" -o json', print_command_to_run = False, print_errors = False)
237-
238-
if afd_output.success and afd_output.json_data:
239-
afd_name = afd_output.json_data.get('name')
240-
print(f'✅ Azure Front Door verified: {afd_name}')
241-
242-
# Check Container Apps if enabled
243-
if use_aca:
244-
aca_output = utils.run(f'az containerapp list -g {rg_name} --query "length(@)"', print_command_to_run = False, print_errors = False)
245-
246-
if aca_output.success:
247-
aca_count = int(aca_output.text.strip())
248-
print(f'✅ Container Apps verified: {aca_count} app(s) created')
249-
250-
print('\n🎉 Infrastructure verification completed successfully!')
251-
return True
252-
253-
else:
254-
print('\n❌ APIM service not found!')
255-
return False
256-
257-
except Exception as e:
258-
print(f'\n⚠️ Verification failed with error: {str(e)}')
259-
return False
260-
123+
return []
261124
def main():
262125
"""
263126
Main entry point for command-line usage.
264127
"""
265128

266-
parser = argparse.ArgumentParser(description='Create AFD-APIM-PE infrastructure')
267-
parser.add_argument('--location', default='eastus2', help='Azure region (default: eastus2)')
268-
parser.add_argument('--index', type=int, help='Infrastructure index')
269-
parser.add_argument('--sku', choices=['Standardv2', 'Premiumv2'], default='Standardv2', help='APIM SKU (default: Standardv2)')
270-
parser.add_argument('--no-aca', action='store_true', help='Disable Azure Container Apps')
271-
129+
parser = argparse.ArgumentParser(description = 'Create AFD-APIM-PE infrastructure')
130+
parser.add_argument('--location', default = 'eastus2', help = 'Azure region (default: eastus2)')
131+
parser.add_argument('--index', type = int, help = 'Infrastructure index')
132+
parser.add_argument('--sku', choices = ['Standardv2', 'Premiumv2'], default = 'Standardv2', help = 'APIM SKU (default: Standardv2)')
133+
parser.add_argument('--no-aca', action = 'store_true', help = 'Disable Azure Container Apps')
272134
args = parser.parse_args()
273-
274-
# Convert SKU string to enum
275-
sku_map = {
276-
'Standardv2': APIM_SKU.STANDARDV2,
277-
'Premiumv2': APIM_SKU.PREMIUMV2
278-
}
279-
135+
136+
# Convert SKU string to enum using the enum's built-in functionality
280137
try:
281-
result = _create_afd_apim_pe_infrastructure(
282-
rg_location = args.location,
283-
index = args.index,
284-
apim_sku = sku_map[args.sku],
285-
use_aca = not args.no_aca
286-
)
287-
288-
if result.success:
289-
print('\n🎉 Infrastructure creation completed successfully!')
290-
sys.exit(0)
291-
else:
292-
print('\n💥 Infrastructure creation failed!')
293-
sys.exit(1)
294-
295-
except Exception as e:
296-
print(f'\n💥 Error: {str(e)}')
138+
apim_sku = APIM_SKU(args.sku)
139+
except ValueError:
140+
print(f"Error: Invalid SKU '{args.sku}'. Valid options are: {', '.join([sku.value for sku in APIM_SKU])}")
297141
sys.exit(1)
298142

143+
create_infrastructure(args.location, args.index, apim_sku, args.no_aca)
299144

300145
if __name__ == '__main__':
301146
main()

infrastructure/afd-apim-pe/main.bicep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ module afdModule '../../shared/bicep/modules/afd/v1/afd.bicep' = {
292292
fdeName: afdEndpointName
293293
afdSku: 'Premium_AzureFrontDoor'
294294
apimName: apimName
295+
logAnalyticsWorkspaceId: lawId
295296
}
296297
dependsOn: [
297298
apimModule

infrastructure/apim-aca/create.ipynb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,20 @@
2020
"import utils\n",
2121
"from apimtypes import *\n",
2222
"\n",
23-
"# User-defined parameters (change these as needed)\n",
24-
"rg_location = 'eastus2' # Azure region for deployment\n",
25-
"index = 1 # Infrastructure index (use different numbers for multiple environments)\n",
26-
"apim_sku = APIM_SKU.BASICV2 # Options: 'BASICV2', 'STANDARDV2', 'PREMIUMV2'\n",
27-
"reveal_backend = True # Set to True to reveal the backend details in the API operations\n",
23+
"# ------------------------------\n",
24+
"# USER CONFIGURATION\n",
25+
"# ------------------------------\n",
26+
"\n",
27+
"rg_location = 'eastus2' # Azure region for deployment\n",
28+
"index = 1 # Infrastructure index (use different numbers for multiple environments)\n",
29+
"apim_sku = APIM_SKU.BASICV2 # Options: 'BASICV2', 'STANDARDV2', 'PREMIUMV2'\n",
30+
"\n",
31+
"\n",
32+
"\n",
33+
"# ------------------------------\n",
34+
"# SYSTEM CONFIGURATION\n",
35+
"# ------------------------------\n",
2836
"\n",
29-
"# Create an instance of the desired infrastructure\n",
3037
"inb_helper = utils.InfrastructureNotebookHelper(rg_location, INFRASTRUCTURE.APIM_ACA, index, apim_sku) \n",
3138
"success = inb_helper.create_infrastructure()\n",
3239
"\n",

0 commit comments

Comments
 (0)