Skip to content

Commit 8a74f59

Browse files
feat: Add VM size recommendations for Bastion and Jumpbox subnets
2 parents 47e087c + dafa23d commit 8a74f59

5 files changed

Lines changed: 11066 additions & 44 deletions

File tree

docs/CustomizingAzdParameters.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,17 @@ By default this template will use the environment name as the prefix to prevent
1818
| `AZURE_OPENAI_IMAGE_MODEL` | string | `gpt-image-1-mini` | Image model to deploy (allowed: `gpt-image-1-mini`, `gpt-image-1.5`, `none`). |
1919
| `IMAGE_MODEL_CAPACITY` | integer | `1` | Sets the image model deployment capacity in RPM (minimum: `1`). |
2020
| `AZURE_OPENAI_API_VERSION` | string | `2025-01-01-preview` | Specifies the API version for Azure OpenAI service. |
21-
| `AZURE_ENV_OPENAI_LOCATION` | string | `<User selects during deployment>` | Sets the Azure region for OpenAI resource deployment. |
21+
| `AZURE_ENV_OPENAI_LOCATION` | string | `<User selects during deployment>` | Sets the Azure region for OpenAI resource deployment. Allowed: `australiaeast`, `canadaeast`, `eastus2`, `japaneast`, `koreacentral`, `polandcentral`, `swedencentral`, `switzerlandnorth`, `uaenorth`, `uksouth`, `westus3`. |
2222
| `AZURE_ENV_LOG_ANALYTICS_WORKSPACE_ID` | string | `""` | Reuses an existing Log Analytics Workspace instead of creating a new one. |
2323
| `AZURE_EXISTING_AI_PROJECT_RESOURCE_ID`| string | `""` | Reuses an existing AI Foundry Project instead of creating a new one. |
24+
| `enableMonitoring` | boolean | `false` | Enable Log Analytics and Application Insights (WAF-aligned). |
25+
| `enableScalability` | boolean | `false` | Enable auto-scaling and higher SKUs (WAF-aligned). |
26+
| `enableRedundancy` | boolean | `false` | Enable zone redundancy and geo-replication (WAF-aligned). |
27+
| `enablePrivateNetworking` | boolean | `false` | Enable VNet integration and private endpoints (WAF-aligned). |
28+
| `deployBastionAndJumpbox` | boolean | `false` | Deploy Azure Bastion and jumpbox admin-path resources when private networking is enabled. |
29+
| `AZURE_ENV_VM_SIZE` | string | `""` | Overrides the jumpbox VM size (private networking only). Must support accelerated networking and Premium SSD. |
30+
| `AZURE_ENV_VM_ADMIN_USERNAME` | string | `""` | Sets the jumpbox VM admin username (private networking only). |
31+
| `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `""` | Sets the jumpbox VM admin password. Bastion and jumpbox resources are deployed only when this is set and `deployBastionAndJumpbox=true`. |
2432
| `ACR_NAME` | string | `contentgencontainerreg` | Sets the existing Azure Container Registry name (without `.azurecr.io`). |
2533
| `IMAGE_TAG` | string | `latest` | Sets the container image tag (e.g., `latest`, `dev`, `hotfix`). |
2634

infra/main.bicep

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,19 @@ param existingLogAnalyticsWorkspaceId string = ''
107107
@description('Optional. Resource ID of an existing Foundry project.')
108108
param azureExistingAIProjectResourceId string = ''
109109

110-
@description('Optional. Deploy Azure Bastion and Jumpbox VM for private network administration.')
110+
@description('Optional. Deploy Azure Bastion and Jumpbox resources for private network administration.')
111111
param deployBastionAndJumpbox bool = false
112112

113+
@description('Optional. Jumpbox VM size. Must support accelerated networking and Premium SSD.')
114+
param vmSize string = ''
115+
116+
@description('Optional. Jumpbox VM admin username.')
117+
param vmAdminUsername string = ''
118+
119+
@description('Optional. Jumpbox VM admin password.')
120+
@secure()
121+
param vmAdminPassword string = ''
122+
113123
@description('Optional. The tags to apply to all deployed Azure resources.')
114124
param tags object = {}
115125

@@ -367,17 +377,111 @@ module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-id
367377
}
368378

369379
// ========== Virtual Network and Networking Components ========== //
380+
var deployAdminAccessResources = enablePrivateNetworking && deployBastionAndJumpbox && !empty(vmAdminPassword)
370381
module virtualNetwork 'modules/virtualNetwork.bicep' = if (enablePrivateNetworking) {
371382
name: take('module.virtualNetwork.${solutionSuffix}', 64)
372383
params: {
373384
vnetName: 'vnet-${solutionSuffix}'
374-
vnetLocation: solutionLocation
375-
vnetAddressPrefixes: ['10.0.0.0/20']
385+
addressPrefixes: ['10.0.0.0/20'] // 4096 addresses (enough for 8 /23 subnets or 16 /24)
386+
location: solutionLocation
387+
deployBastionAndJumpbox: deployAdminAccessResources
376388
tags: tags
377389
logAnalyticsWorkspaceId: logAnalyticsWorkspaceResourceId
378-
enableTelemetry: enableTelemetry
379390
resourceSuffix: solutionSuffix
380-
deployBastionAndJumpbox: deployBastionAndJumpbox
391+
enableTelemetry: enableTelemetry
392+
}
393+
}
394+
395+
// Azure Bastion Host
396+
var bastionHostName = 'bas-${solutionSuffix}'
397+
var zoneSupportedJumpboxLocations = [
398+
'australiaeast'
399+
'centralus'
400+
'eastus'
401+
'eastus2'
402+
'japaneast'
403+
'northeurope'
404+
'southeastasia'
405+
'swedencentral'
406+
'uksouth'
407+
'westus3'
408+
]
409+
module bastionHost 'br/public:avm/res/network/bastion-host:0.8.2' = if (deployAdminAccessResources) {
410+
name: take('avm.res.network.bastion-host.${bastionHostName}', 64)
411+
params: {
412+
name: bastionHostName
413+
skuName: 'Standard'
414+
location: solutionLocation
415+
virtualNetworkResourceId: virtualNetwork!.outputs.resourceId
416+
diagnosticSettings: !empty(logAnalyticsWorkspaceResourceId)
417+
? [
418+
{
419+
name: 'bastionDiagnostics'
420+
workspaceResourceId: logAnalyticsWorkspaceResourceId
421+
logCategoriesAndGroups: [
422+
{
423+
categoryGroup: 'allLogs'
424+
enabled: true
425+
}
426+
]
427+
}
428+
]
429+
: []
430+
tags: tags
431+
enableTelemetry: enableTelemetry
432+
publicIPAddressObject: {
433+
name: 'pip-${bastionHostName}'
434+
}
435+
}
436+
}
437+
438+
// Jumpbox Virtual Machine
439+
var jumpboxUniqueToken = take(uniqueString(resourceGroup().id, solutionSuffix), 10)
440+
var jumpboxVmName = take('vm-${jumpboxUniqueToken}', 15)
441+
module jumpboxVM 'br/public:avm/res/compute/virtual-machine:0.21.0' = if (deployAdminAccessResources) {
442+
name: take('avm.res.compute.virtual-machine.${jumpboxVmName}', 64)
443+
params: {
444+
name: take(jumpboxVmName, 15)
445+
enableTelemetry: enableTelemetry
446+
computerName: take(jumpboxVmName, 15)
447+
osType: 'Windows'
448+
vmSize: empty(vmSize) ? 'Standard_D2s_v5' : vmSize
449+
adminUsername: empty(vmAdminUsername) ? 'JumpboxAdminUser' : vmAdminUsername
450+
adminPassword: vmAdminPassword
451+
managedIdentities: {
452+
userAssignedResourceIds: [
453+
userAssignedIdentity.outputs.resourceId
454+
]
455+
}
456+
availabilityZone: contains(zoneSupportedJumpboxLocations, solutionLocation) ? 1 : -1
457+
imageReference: {
458+
publisher: 'microsoft-dsvm'
459+
offer: 'dsvm-win-2022'
460+
sku: 'winserver-2022'
461+
version: 'latest'
462+
}
463+
nicConfigurations: [
464+
{
465+
name: 'nic-${jumpboxVmName}'
466+
enableAcceleratedNetworking: true
467+
ipConfigurations: [
468+
{
469+
name: 'ipconfig01'
470+
subnetResourceId: virtualNetwork!.outputs.jumpboxSubnetResourceId
471+
}
472+
]
473+
}
474+
]
475+
osDisk: {
476+
caching: 'ReadWrite'
477+
diskSizeGB: 128
478+
managedDisk: {
479+
storageAccountType: 'Premium_LRS'
480+
}
481+
}
482+
encryptionAtHost: false // Some Azure subscriptions do not support encryption at host
483+
location: solutionLocation
484+
tags: tags
381485
}
382486
dependsOn: (enableMonitoring && !useExistingLogAnalytics) ? [logAnalyticsWorkspace] : []
383487
}

0 commit comments

Comments
 (0)