az-hop/bicep/azhop.bicep

1169 строки
53 KiB
Bicep

targetScope = 'resourceGroup'
var azhopResourceGroupName = resourceGroup().name
@description('Azure region to use')
param location string
@description('Branch name to deploy from - Default main')
param branchName string = 'main'
@description('Autogenerate passwords and SSH key pair.')
param autogenerateSecrets bool = false
@description('SSH Public Key for the Virtual Machines.')
@secure()
param adminSshPublicKey string = ''
@description('SSH Private Key for the Virtual Machines.')
@secure()
param adminSshPrivateKey string = ''
@description('The Windows/Active Directory password.')
@secure()
param adminPassword string = ''
@description('Password for the Slurm accounting admin user')
@secure()
param databaseAdminPassword string = ''
@description('Identity of the deployer if not deploying from a deployer VM')
param loggedUserObjectId string = ''
@description('Input configuration file in json format')
param azhopConfig object
var resourcePostfix = '${uniqueString(subscription().subscriptionId, azhopResourceGroupName)}x'
// Local variables to help in the simplication as functions doesn't exists
var enablePublicIP = contains(azhopConfig, 'locked_down_network') ? azhopConfig.locked_down_network.public_ip : true
var jumpboxSshPort = deployJumpbox ? (contains(azhopConfig.jumpbox, 'ssh_port') ? azhopConfig.jumpbox.ssh_port : 22) : 22
var deployerSshPort = deployDeployer ? (contains(azhopConfig.deployer, 'ssh_port') ? azhopConfig.deployer.ssh_port : 22) : 22
var ccportalSshPort = cycleCloudAsDeployer ? (contains(azhopConfig.cyclecloud, 'ssh_port') ? azhopConfig.cyclecloud.ssh_port : 22) : 22
var incomingSSHPort = deployDeployer ? deployerSshPort : (cycleCloudAsDeployer ? ccportalSshPort : jumpboxSshPort )
var deployLustre = contains(azhopConfig, 'lustre') && contains(azhopConfig.lustre, 'create') ? azhopConfig.lustre.create : false
var deployJumpbox = contains(azhopConfig, 'jumpbox') ? true : false
var deployDeployer = contains(azhopConfig, 'deployer') ? true : false
var deployGrafana = contains(azhopConfig, 'monitoring') && contains(azhopConfig.monitoring, 'grafana') ? azhopConfig.monitoring.grafana : true
var deployOnDemand = contains(azhopConfig, 'ondemand') ? true : false
var cycleCloudAsDeployer = contains(azhopConfig, 'cyclecloud') && contains(azhopConfig.cyclecloud, 'use_as_deployer') ? azhopConfig.cyclecloud.use_as_deployer : false
var useExistingAD = contains(azhopConfig, 'domain') ? azhopConfig.domain.use_existing_dc : false
var userAuth = contains(azhopConfig, 'authentication') && contains(azhopConfig.authentication, 'user_auth') ? azhopConfig.authentication.user_auth : 'ad'
var createAD = ! useExistingAD && (userAuth == 'ad')
var highAvailabilityForAD = contains(azhopConfig, 'ad') && contains(azhopConfig.ad, 'high_availability') ? azhopConfig.ad.high_availability : false
var linuxBaseImage = contains(azhopConfig, 'linux_base_image') ? azhopConfig.linux_base_image : 'OpenLogic:CentOS:7_9-gen2:latest'
var linuxBasePlan = contains(azhopConfig, 'linux_base_plan') ? azhopConfig.linux_base_plan : ''
var windowsBaseImage = contains(azhopConfig, 'windows_base_image') ? azhopConfig.windows_base_image : 'MicrosoftWindowsServer:WindowsServer:2019-Datacenter-smalldisk:latest'
var cyclecloudBaseImage = contains(azhopConfig.cyclecloud, 'image') ? azhopConfig.cyclecloud.image : linuxBaseImage
var cyclecloudBasePlan = contains(azhopConfig.cyclecloud, 'plan') ? azhopConfig.cyclecloud.plan : linuxBasePlan
var createDatabase = queue_manager == 'slurm' && slurm_accounting_enabled
var slurm_accounting_enabled = contains(azhopConfig, 'slurm') && contains(azhopConfig.slurm, 'accounting_enabled') ? azhopConfig.slurm.accounting_enabled : false
var computeMIoption = contains(azhopConfig, 'compute_vm_identity')
var createComputeMI = computeMIoption && contains(azhopConfig.compute_vm_identity, 'create') ? azhopConfig.compute_vm_identity.create : false
var computeMIname = computeMIoption && contains(azhopConfig.compute_vm_identity, 'name') ? azhopConfig.compute_vm_identity.name : 'compute-mi'
var existingComputeMIrg = !createComputeMI && computeMIoption && contains(azhopConfig.compute_vm_identity, 'resource_group') ? azhopConfig.compute_vm_identity.resource_group : ''
var create_anf = contains(azhopConfig, 'anf') && contains(azhopConfig.anf, 'create') ? azhopConfig.anf.create : (contains(azhopConfig, 'anf') ? true : false)
var nsgTargetForDC = {
type: useExistingAD ? 'ips' : 'asg'
target: useExistingAD ? azhopConfig.domain.existing_dc_details.domain_controller_ip_addresses : 'asg-ad'
}
var vmNamesMap = {
ad : contains(azhopConfig, 'ad') && contains(azhopConfig.ad, 'name') ? azhopConfig.ad.name : 'ad'
ad2 : contains(azhopConfig, 'ad') && contains(azhopConfig.ad, 'ha_name') ? azhopConfig.ad.ha_name : 'ad2'
deployer: contains(azhopConfig, 'deployer') && contains(azhopConfig.deployer, 'name') ? azhopConfig.deployer.name : 'deployer'
jumpbox: contains(azhopConfig, 'jumpbox') && contains(azhopConfig.jumpbox, 'name') ? azhopConfig.jumpbox.name : 'jumpbox'
ccportal: contains(azhopConfig, 'cyclecloud') && contains(azhopConfig.cyclecloud, 'name') ? azhopConfig.cyclecloud.name : 'ccportal'
ondemand: contains(azhopConfig, 'ondemand') && contains(azhopConfig.ondemand, 'name') ? azhopConfig.ondemand.name : 'ondemand'
scheduler: contains(azhopConfig, 'scheduler') && contains(azhopConfig.scheduler, 'name') ? azhopConfig.scheduler.name : 'scheduler'
grafana: contains(azhopConfig, 'grafana') && contains(azhopConfig.grafana, 'name') ? azhopConfig.grafana.name : 'grafana'
}
var queue_manager= contains(azhopConfig, 'queue_manager') ? azhopConfig.queue_manager : 'openpbs'
// Convert the azhop configuration file to a pivot format used for the deployment
var config = {
admin_user: azhopConfig.admin_user
keyvault_readers: contains(azhopConfig, 'key_vault_readers') ? ( empty(azhopConfig.key_vault_readers) ? [] : [ azhopConfig.key_vault_readers ] ) : []
public_ip: enablePublicIP
deploy_gateway: contains(azhopConfig, 'vpn_gateway') && contains(azhopConfig.vpn_gateway, 'create') ? azhopConfig.vpn_gateway.create : false
deploy_bastion: contains(azhopConfig, 'bastion') && contains(azhopConfig.bastion, 'create') ? azhopConfig.bastion.create : false
deploy_lustre: deployLustre
lock_down_network: {
enforce: contains(azhopConfig, 'locked_down_network') && contains(azhopConfig.locked_down_network, 'enforce') ? azhopConfig.locked_down_network.enforce : false
grant_access_from: contains(azhopConfig, 'locked_down_network') && contains(azhopConfig.locked_down_network, 'grant_access_from') ? ( empty(azhopConfig.locked_down_network.grant_access_from) ? [] : [ azhopConfig.locked_down_network.grant_access_from ] ) : []
}
nat_gateway: {
create: contains(azhopConfig, 'nat_gateway') && contains(azhopConfig.nat_gateway, 'create') ? azhopConfig.nat_gateway.create : false
name: contains(azhopConfig, 'nat_gateway') && contains(azhopConfig.nat_gateway, 'name') ? azhopConfig.nat_gateway.name : 'natgw-${resourcePostfix}'
}
queue_manager: queue_manager
slurm: {
admin_user: contains(azhopConfig, 'database') && contains(azhopConfig.database, 'user') ? azhopConfig.database.user : 'sqladmin'
accounting_enabled: contains(azhopConfig, 'slurm') && contains(azhopConfig.slurm, 'accounting_enabled') ? azhopConfig.slurm.accounting_enabled : false
}
private_dns: {
create: contains(azhopConfig, 'private_dns') && contains(azhopConfig.private_dns, 'create') ? azhopConfig.private_dns.create : false
name: contains(azhopConfig, 'private_dns') && contains(azhopConfig.private_dns, 'name') ? azhopConfig.private_dns.name : 'hpc.azure'
registration_enabled: contains(azhopConfig, 'private_dns') && contains(azhopConfig.private_dns, 'registration_enabled') ? azhopConfig.private_dns.registration_enabled : false
}
domain: {
name : contains(azhopConfig, 'domain') ? azhopConfig.domain.name : 'hpc.azure'
domain_join_user: createAD ? {
username: azhopConfig.admin_user
password_key_vault_name: 'foo'
password_key_vault_resource_group_name: 'foo'
password_key_vault_secret_name: 'foo'
} : useExistingAD ? {
username: azhopConfig.domain.domain_join_user.username
password_key_vault_name: azhopConfig.domain.domain_join_user.password_key_vault_name
password_key_vault_resource_group_name: azhopConfig.domain.domain_join_user.password_key_vault_resource_group_name
password_key_vault_secret_name: azhopConfig.domain.domain_join_user.password_key_vault_secret_name
} : {
username: ''
}
domain_controlers : createAD ? (! highAvailabilityForAD ? [vmNamesMap.ad] : [vmNamesMap.ad, vmNamesMap.ad2]) : useExistingAD ? azhopConfig.domain.existing_dc_details.domain_controller_names : []
ldap_server: createAD ? vmNamesMap.ad : useExistingAD ? azhopConfig.domain.existing_dc_details.domain_controller_names[0] : ''
}
key_vault_name: contains(azhopConfig, 'azure_key_vault') ? azhopConfig.azure_key_vault.name : 'kv${resourcePostfix}'
storage_account_name: contains(azhopConfig, 'azure_storage_account') ? azhopConfig.azure_storage_account.name : 'azhop${resourcePostfix}'
db_name: contains(azhopConfig, 'database') && contains(azhopConfig.database, 'name') ? azhopConfig.database.name : 'mysql-${resourcePostfix}'
deploy_grafana: deployGrafana
deploy_ondemand: deployOnDemand
deploy_sig: contains(azhopConfig, 'image_gallery') && contains(azhopConfig.image_gallery, 'create') ? azhopConfig.image_gallery.create : false
// Default home directory is ANF
homedir_type: contains(azhopConfig.mounts.home, 'type') ? azhopConfig.mounts.home.type : 'existing'
homedir_mountpoint: azhopConfig.mounts.home.mountpoint
lustre: {
create: deployLustre ? true : false
sku: contains(azhopConfig, 'lustre') && contains(azhopConfig.lustre, 'sku') ? azhopConfig.lustre.sku : 'AMLFS-Durable-Premium-250'
capacity: contains(azhopConfig, 'lustre') && contains(azhopConfig.lustre, 'capacity') ? azhopConfig.lustre.capacity : 8
}
anf: {
create: create_anf
dual_protocol: contains(azhopConfig, 'anf') && contains(azhopConfig.anf, 'dual_protocol') ? azhopConfig.anf.dual_protocol : false
service_level: contains(azhopConfig, 'anf') && contains(azhopConfig.anf, 'homefs_service_level') ? azhopConfig.anf.homefs_service_level : 'Standard'
size_gb: contains(azhopConfig, 'anf') && contains(azhopConfig.anf, 'homefs_size_tb') ? azhopConfig.anf.homefs_size_tb*1024 : 4096
}
azurefiles: {
create: contains(azhopConfig, 'azurefiles') && contains(azhopConfig.azurefiles, 'create') ? azhopConfig.azurefiles.create : false
size_gb: contains(azhopConfig, 'azurefiles') && contains(azhopConfig.azurefiles, 'size_gb') ? azhopConfig.azurefiles.size_gb : 1024
}
vnet: {
tags: contains(azhopConfig.network.vnet,'tags') ? azhopConfig.network.vnet.tags : {}
name: azhopConfig.network.vnet.name
cidr: azhopConfig.network.vnet.address_space
subnets: union (
{
frontend: {
name: contains(azhopConfig.network.vnet.subnets.frontend, 'name') ? azhopConfig.network.vnet.subnets.frontend.name : 'frontend'
cidr: azhopConfig.network.vnet.subnets.frontend.address_prefixes
nat_gateway: true
service_endpoints: [
'Microsoft.Storage'
]
}
admin: {
name: contains(azhopConfig.network.vnet.subnets.admin, 'name') ? azhopConfig.network.vnet.subnets.admin.name : 'admin'
cidr: azhopConfig.network.vnet.subnets.admin.address_prefixes
nat_gateway: true
service_endpoints: [
'Microsoft.KeyVault'
'Microsoft.Storage'
]
}
compute: {
name: contains(azhopConfig.network.vnet.subnets.compute, 'name') ? azhopConfig.network.vnet.subnets.compute.name : 'compute'
cidr: azhopConfig.network.vnet.subnets.compute.address_prefixes
nat_gateway: true
service_endpoints: [
'Microsoft.Storage'
]
}
},
createDatabase ? {
database: {
name: contains(azhopConfig.network.vnet.subnets.database, 'name') ? azhopConfig.network.vnet.subnets.database.name : 'database'
cidr: azhopConfig.network.vnet.subnets.database.address_prefixes
delegations: [
'Microsoft.DBforMySQL/flexibleServers'
]
}
} : {},
create_anf ? {
netapp: {
name: contains(azhopConfig.network.vnet.subnets.netapp, 'name') ? azhopConfig.network.vnet.subnets.netapp.name : 'netapp'
cidr: azhopConfig.network.vnet.subnets.netapp.address_prefixes
delegations: [
'Microsoft.Netapp/volumes'
]
}
} : {},
deployLustre ? {
lustre: {
name: contains(azhopConfig.network.vnet.subnets.lustre, 'name') ? azhopConfig.network.vnet.subnets.lustre.name : 'lustre'
cidr: azhopConfig.network.vnet.subnets.lustre.address_prefixes
}
} : {},
createAD ? {
ad: {
name: contains(azhopConfig.network.vnet.subnets.ad, 'name') ? azhopConfig.network.vnet.subnets.ad.name : 'ad'
cidr: azhopConfig.network.vnet.subnets.ad.address_prefixes
nat_gateway: true
}
} : {},
contains(azhopConfig.network.vnet.subnets,'bastion') ? {
bastion: {
apply_nsg: true
name: 'AzureBastionSubnet'
cidr: azhopConfig.network.vnet.subnets.bastion.address_prefixes
}
} : {},
contains(azhopConfig.network.vnet.subnets,'outbounddns') ? {
outbounddns: {
name: contains(azhopConfig.network.vnet.subnets.outbounddns, 'name') ? azhopConfig.network.vnet.subnets.outbounddns.name : 'outbounddns'
cidr: azhopConfig.network.vnet.subnets.outbounddns.address_prefixes
delegations: [
'Microsoft.Network/dnsResolvers'
]
}
} : {},
contains(azhopConfig.network.vnet.subnets,'gateway') ? {
gateway: {
apply_nsg: false
name: 'GatewaySubnet'
cidr: azhopConfig.network.vnet.subnets.gateway.address_prefixes
}
} : {}
)
peerings: contains(azhopConfig.network,'peering') ? azhopConfig.network.peering : []
}
images: {
ubuntu: {
ref: {
publisher: 'Canonical'
offer: '0001-com-ubuntu-server-focal'
sku: '20_04-lts-gen2'
version: 'latest'
}
}
linux_base: {
plan: linuxBasePlan
ref: contains(linuxBaseImage, '/') ? {
id: linuxBaseImage
} : {
publisher: split(linuxBaseImage,':')[0]
offer: split(linuxBaseImage,':')[1]
sku: split(linuxBaseImage,':')[2]
version: split(linuxBaseImage,':')[3]
}
}
win_base: {
ref: contains(windowsBaseImage, '/') ? {
id: windowsBaseImage
} : {
publisher: split(windowsBaseImage,':')[0]
offer: split(windowsBaseImage,':')[1]
sku: split(windowsBaseImage,':')[2]
version: split(windowsBaseImage,':')[3]
}
}
cyclecloud_base: {
plan: cyclecloudBasePlan
ref: contains(cyclecloudBaseImage, '/') ? {
id: cyclecloudBaseImage
} : {
publisher: split(cyclecloudBaseImage,':')[0]
offer: split(cyclecloudBaseImage,':')[1]
sku: split(cyclecloudBaseImage,':')[2]
version: split(cyclecloudBaseImage,':')[3]
}
}
}
vms: union(
deployOnDemand ? {
ondemand: {
subnet: 'frontend'
name: vmNamesMap.ondemand
sku: azhopConfig.ondemand.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'linux_base'
pip: enablePublicIP
asgs: union(
[ 'asg-ssh', 'asg-ondemand', 'asg-nfs-client', 'asg-sched', 'asg-cyclecloud-client' ],
deployGrafana ? ['asg-telegraf'] : [],
(userAuth == 'ad') ? ['asg-ad-client'] : [],
deployLustre ? [ 'asg-lustre-client' ] : []
)
}
} : {},
{
ccportal: {
subnet: deployOnDemand ? 'admin' : 'frontend'
name: vmNamesMap.ccportal
sku: azhopConfig.cyclecloud.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'cyclecloud_base'
pip: enablePublicIP && !deployOnDemand
sshPort: cycleCloudAsDeployer ? incomingSSHPort : 22
deploy_script: cycleCloudAsDeployer ? replace(replace(loadTextContent('install.sh'), '__INSERT_AZHOP_BRANCH__', branchName), '__SSH_PORT__', string(incomingSSHPort)) : ''
datadisks: [
{
name: '${vmNamesMap.ccportal}-datadisk0'
disksku: 'Premium_LRS'
size: split(cyclecloudBaseImage,':')[0] == 'azurecyclecloud' ? 0 : 128
caching: 'ReadWrite'
createOption: split(cyclecloudBaseImage,':')[0] == 'azurecyclecloud' ? 'FromImage' : 'Empty'
}
]
identity: {
keyvault: cycleCloudAsDeployer ? {
secret_permissions: [ 'All' ]
} : {}
roles: [
'Contributor'
]
}
asgs: union(
[ 'asg-ssh', 'asg-cyclecloud' ],
cycleCloudAsDeployer ? [ 'asg-jumpbox', 'asg-deployer' ] : [],
(userAuth == 'ad') ? ['asg-ad-client'] : [],
deployGrafana ? ['asg-telegraf'] : []
)
}
scheduler: {
subnet: 'admin'
name: vmNamesMap.scheduler
sku: azhopConfig.scheduler.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'linux_base'
asgs: union(
[ 'asg-ssh', 'asg-sched', 'asg-cyclecloud-client', 'asg-nfs-client' ],
(userAuth == 'ad') ? ['asg-ad-client'] : [],
deployGrafana ? ['asg-telegraf'] : [],
createDatabase ? ['asg-mysql-client'] : []
)
}
},
createAD ? {
ad: {
subnet: 'ad'
windows: true
name: vmNamesMap.ad
ahub: contains(azhopConfig.ad, 'hybrid_benefit') ? azhopConfig.ad.hybrid_benefit : false
sku: azhopConfig.ad.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'win_base'
asgs: [ 'asg-ad', 'asg-rdp', 'asg-ad-client' ]
}
} : {},
deployDeployer ? {
deployer: {
subnet: 'frontend'
name: vmNamesMap.deployer
sku: azhopConfig.deployer.vm_size
osdisksku: 'Standard_LRS'
image: 'ubuntu'
pip: enablePublicIP
sshPort: incomingSSHPort
asgs: union(
[ 'asg-ssh', 'asg-jumpbox', 'asg-deployer' ],
deployGrafana ? ['asg-telegraf'] : []
)
deploy_script: replace(replace(loadTextContent('install.sh'), '__INSERT_AZHOP_BRANCH__', branchName), '__SSH_PORT__', string(incomingSSHPort))
identity: {
keyvault: {
secret_permissions: [ 'All' ]
}
roles: [
'Contributor'
'UserAccessAdministrator'
]
}
}
} : {},
highAvailabilityForAD ? {
ad2: {
subnet: 'ad'
windows: true
ahub: contains(azhopConfig.ad, 'hybrid_benefit') ? azhopConfig.ad.hybrid_benefit : false
name: vmNamesMap.ad2
sku: azhopConfig.ad.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'win_base'
asgs: [ 'asg-ad', 'asg-rdp', 'asg-ad-client' ]
}
} : {} ,
deployJumpbox ? {
jumpbox: {
subnet: 'frontend'
name: vmNamesMap.jumpbox
sku: azhopConfig.jumpbox.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'linux_base'
pip: enablePublicIP
sshPort: incomingSSHPort
asgs: union(
[ 'asg-ssh', 'asg-jumpbox' ],
deployGrafana ? ['asg-telegraf'] : []
)
deploy_script: incomingSSHPort != 22 ? replace(loadTextContent('jumpbox.yml'), '__SSH_PORT__', string(incomingSSHPort)) : ''
}
} : {},
deployGrafana ? {
grafana: {
subnet: 'admin'
name: vmNamesMap.grafana
sku: azhopConfig.grafana.vm_size
osdisksku: 'StandardSSD_LRS'
image: 'linux_base'
asgs: union (
[ 'asg-ssh', 'asg-grafana', 'asg-telegraf', 'asg-nfs-client' ],
(userAuth == 'ad') ? ['asg-ad-client'] : []
)
}
} : {}
)
asg_names: union([ 'asg-ssh', 'asg-jumpbox', 'asg-sched', 'asg-cyclecloud', 'asg-cyclecloud-client', 'asg-nfs-client' ],
deployLustre ? [ 'asg-lustre-client' ] : [],
deployGrafana ? [ 'asg-grafana', 'asg-telegraf' ] : [],
(userAuth == 'ad') ? ['asg-rdp', 'asg-ad', 'asg-ad-client'] : [],
deployOnDemand ? ['asg-ondemand']: [],
createDatabase ? ['asg-mysql-client']: [],
deployDeployer || cycleCloudAsDeployer ? ['asg-deployer']: []
)
service_ports: {
All: ['0-65535']
Bastion: (incomingSSHPort == 22) ? ['22', '3389'] : ['22', string(incomingSSHPort), '3389']
Web: ['443', '80']
Ssh: ['22']
HubSsh: [string(incomingSSHPort)]
// DNS, Kerberos, RpcMapper, Ldap, Smb, KerberosPass, LdapSsl, LdapGc, LdapGcSsl, AD Web Services, RpcSam
DomainControlerTcp: ['53', '88', '135', '389', '445', '464', '636', '3268', '3269', '9389', '49152-65535']
// DNS, Kerberos, W32Time, NetBIOS, Ldap, KerberosPass, LdapSsl
DomainControlerUdp: ['53', '88', '123', '138', '389', '464', '636']
// Web, NoVNC, WebSockify
NoVnc: ['80', '443', '5900-5910', '61001-61010']
Dns: ['53']
Rdp: ['3389']
// Pbs: ['6200', '15001-15009', '17001', '32768-61000']
// Slurm: ['6817-6819']
Shed: (queue_manager == 'slurm') ? ['6817-6819', '59000-61000'] : ['6200', '15001-15009', '17001', '32768-61000']
Lustre: ['988', '1019-1023']
Nfs: ['111', '635', '2049', '4045', '4046']
SMB: ['445']
Telegraf: ['8086']
Grafana: ['3000']
// HTTPS, AMQP
CycleCloud: ['9443', '5672']
MySQL: ['3306', '33060']
WinRM: ['5985', '5986']
}
nsg_rules: {
default: {
//
// INBOUND RULES
//
// SSH internal rules
AllowSshFromJumpboxIn : ['320', 'Inbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-jumpbox', 'asg', 'asg-ssh']
AllowSshFromComputeIn : ['330', 'Inbound', 'Allow', 'Tcp', 'Ssh', 'subnet', 'compute', 'asg', 'asg-ssh']
AllowSshToComputeIn : ['360', 'Inbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-ssh', 'subnet', 'compute']
// All communications inside compute subnet
AllowAllComputeComputeIn : ['365', 'Inbound', 'Allow', 'Tcp', 'All', 'subnet', 'compute', 'subnet', 'compute']
// Scheduler
AllowSchedIn : ['369', 'Inbound', 'Allow', '*', 'Shed', 'asg', 'asg-sched', 'asg', 'asg-sched']
// AllowPbsClientIn : ['370', 'Inbound', 'Allow', '*', 'Pbs', 'asg', 'asg-pbs-client', 'asg', 'asg-pbs']
AllowSchedComputeIn : ['380', 'Inbound', 'Allow', '*', 'Shed', 'asg', 'asg-sched', 'subnet', 'compute']
// AllowComputePbsClientIn : ['390', 'Inbound', 'Allow', '*', 'Pbs', 'subnet', 'compute', 'asg', 'asg-pbs-client']
AllowComputeSchedIn : ['400', 'Inbound', 'Allow', '*', 'Shed', 'subnet', 'compute', 'asg', 'asg-sched']
// AllowComputeComputeSchedIn : ['401', 'Inbound', 'Allow', '*', 'Shed', 'subnet', 'compute', 'subnet', 'compute']
// CycleCloud
AllowCycleClientIn : ['450', 'Inbound', 'Allow', 'Tcp', 'CycleCloud', 'asg', 'asg-cyclecloud-client', 'asg', 'asg-cyclecloud']
AllowCycleClientComputeIn : ['460', 'Inbound', 'Allow', 'Tcp', 'CycleCloud', 'subnet', 'compute', 'asg', 'asg-cyclecloud']
AllowCycleServerIn : ['465', 'Inbound', 'Allow', 'Tcp', 'CycleCloud', 'asg', 'asg-cyclecloud', 'asg', 'asg-cyclecloud-client']
// Deny all remaining traffic
DenyVnetInbound : ['3100', 'Inbound', 'Deny', '*', 'All', 'tag', 'VirtualNetwork', 'tag', 'VirtualNetwork']
//
// Outbound
//
// CycleCloud
AllowCycleServerOut : ['300', 'Outbound', 'Allow', 'Tcp', 'CycleCloud', 'asg', 'asg-cyclecloud', 'asg', 'asg-cyclecloud-client']
AllowCycleClientOut : ['310', 'Outbound', 'Allow', 'Tcp', 'CycleCloud', 'asg', 'asg-cyclecloud-client', 'asg', 'asg-cyclecloud']
AllowComputeCycleClientIn : ['320', 'Outbound', 'Allow', 'Tcp', 'CycleCloud', 'subnet', 'compute', 'asg', 'asg-cyclecloud']
// Scheduler
AllowSchedOut : ['340', 'Outbound', 'Allow', '*', 'Shed', 'asg', 'asg-sched', 'asg', 'asg-sched']
// AllowPbsClientOut : ['350', 'Outbound', 'Allow', '*', 'Pbs', 'asg', 'asg-pbs-client', 'asg', 'asg-pbs']
AllowSchedComputeOut : ['360', 'Outbound', 'Allow', '*', 'Shed', 'asg', 'asg-sched', 'subnet', 'compute']
AllowComputeSchedOut : ['370', 'Outbound', 'Allow', '*', 'Shed', 'subnet', 'compute', 'asg', 'asg-sched']
//AllowComputePbsClientOut : ['380', 'Outbound', 'Allow', '*', 'Pbs', 'subnet', 'compute', 'asg', 'asg-pbs-client']
// AllowComputeComputeSchedOut : ['381', 'Outbound', 'Allow', '*', 'Shed', 'subnet', 'compute', 'subnet', 'compute']
// SSH internal rules
AllowSshFromJumpboxOut : ['490', 'Outbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-jumpbox', 'asg', 'asg-ssh']
AllowSshComputeOut : ['500', 'Outbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-ssh', 'subnet', 'compute']
AllowSshFromComputeOut : ['530', 'Outbound', 'Allow', 'Tcp', 'Ssh', 'subnet', 'compute', 'asg', 'asg-ssh']
// All communications inside compute subnet
AllowAllComputeComputeOut : ['540', 'Outbound', 'Allow', 'Tcp', 'All', 'subnet', 'compute', 'subnet', 'compute']
// Admin and Deployment
AllowDnsOut : ['590', 'Outbound', 'Allow', '*', 'Dns', 'tag', 'VirtualNetwork', 'tag', 'VirtualNetwork']
// Deny all remaining traffic and allow Internet access
AllowInternetOutBound : ['3000', 'Outbound', 'Allow', 'Tcp', 'All', 'tag', 'VirtualNetwork', 'tag', 'Internet']
DenyVnetOutbound : ['3100', 'Outbound', 'Deny', '*', 'All', 'tag', 'VirtualNetwork', 'tag', 'VirtualNetwork']
}
internet: {
AllowInternetSshIn : ['200', 'Inbound', 'Allow', 'Tcp', 'HubSsh', 'tag', 'Internet', 'asg', 'asg-jumpbox']
AllowInternetHttpIn : ['210', 'Inbound', 'Allow', 'Tcp', 'Web', 'tag', 'Internet', 'subnet', 'frontend']
}
hub: {
AllowHubSshIn : ['200', 'Inbound', 'Allow', 'Tcp', 'HubSsh', 'tag', 'VirtualNetwork', 'tag', 'VirtualNetwork']
AllowHubHttpIn : ['210', 'Inbound', 'Allow', 'Tcp', 'Web', 'tag', 'VirtualNetwork', 'tag', 'VirtualNetwork']
}
ad: {
// Inbound
// AD communication
AllowAdServerTcpIn : ['220', 'Inbound', 'Allow', 'Tcp', 'DomainControlerTcp', nsgTargetForDC.type, nsgTargetForDC.target, 'asg', 'asg-ad-client']
AllowAdServerUdpIn : ['230', 'Inbound', 'Allow', 'Udp', 'DomainControlerUdp', nsgTargetForDC.type, nsgTargetForDC.target, 'asg', 'asg-ad-client']
AllowAdClientTcpIn : ['240', 'Inbound', 'Allow', 'Tcp', 'DomainControlerTcp', 'asg', 'asg-ad-client', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdClientUdpIn : ['250', 'Inbound', 'Allow', 'Udp', 'DomainControlerUdp', 'asg', 'asg-ad-client', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdServerComputeTcpIn : ['260', 'Inbound', 'Allow', 'Tcp', 'DomainControlerTcp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'compute']
AllowAdServerComputeUdpIn : ['270', 'Inbound', 'Allow', 'Udp', 'DomainControlerUdp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'compute']
AllowAdClientComputeTcpIn : ['280', 'Inbound', 'Allow', 'Tcp', 'DomainControlerTcp', 'subnet', 'compute', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdClientComputeUdpIn : ['290', 'Inbound', 'Allow', 'Udp', 'DomainControlerUdp', 'subnet', 'compute', nsgTargetForDC.type, nsgTargetForDC.target]
AllowWinRMIn : ['520', 'Inbound', 'Allow', 'Tcp', 'WinRM', 'asg', 'asg-jumpbox', 'asg', 'asg-rdp']
AllowRdpIn : ['550', 'Inbound', 'Allow', 'Tcp', 'Rdp', 'asg', 'asg-jumpbox', 'asg', 'asg-rdp']
// Outbound
// AD communication
AllowAdClientTcpOut : ['200', 'Outbound', 'Allow', 'Tcp', 'DomainControlerTcp', 'asg', 'asg-ad-client', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdClientUdpOut : ['210', 'Outbound', 'Allow', 'Udp', 'DomainControlerUdp', 'asg', 'asg-ad-client', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdClientComputeTcpOut : ['220', 'Outbound', 'Allow', 'Tcp', 'DomainControlerTcp', 'subnet', 'compute', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdClientComputeUdpOut : ['230', 'Outbound', 'Allow', 'Udp', 'DomainControlerUdp', 'subnet', 'compute', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdServerTcpOut : ['240', 'Outbound', 'Allow', 'Tcp', 'DomainControlerTcp', nsgTargetForDC.type, nsgTargetForDC.target, 'asg', 'asg-ad-client']
AllowAdServerUdpOut : ['250', 'Outbound', 'Allow', 'Udp', 'DomainControlerUdp', nsgTargetForDC.type, nsgTargetForDC.target, 'asg', 'asg-ad-client']
AllowAdServerComputeTcpOut : ['260', 'Outbound', 'Allow', 'Tcp', 'DomainControlerTcp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'compute']
AllowAdServerComputeUdpOut : ['270', 'Outbound', 'Allow', 'Udp', 'DomainControlerUdp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'compute']
AllowRdpOut : ['570', 'Outbound', 'Allow', 'Tcp', 'Rdp', 'asg', 'asg-jumpbox', 'asg', 'asg-rdp']
AllowWinRMOut : ['580', 'Outbound', 'Allow', 'Tcp', 'WinRM', 'asg', 'asg-jumpbox', 'asg', 'asg-rdp']
}
ondemand: {
// Inbound
//AllowComputeSlurmIn : ['405', 'Inbound', 'Allow', '*', 'Slurmd', 'asg', 'asg-ondemand', 'subnet', 'compute']
AllowCycleWebIn : ['440', 'Inbound', 'Allow', 'Tcp', 'Web', 'asg', 'asg-ondemand', 'asg', 'asg-cyclecloud']
AllowComputeNoVncIn : ['470', 'Inbound', 'Allow', 'Tcp', 'NoVnc', 'subnet', 'compute', 'asg', 'asg-ondemand']
AllowNoVncComputeIn : ['480', 'Inbound', 'Allow', 'Tcp', 'NoVnc', 'asg', 'asg-ondemand', 'subnet', 'compute']
// Not sure if this is really needed. Why opening web port from deployer to ondemand ?
// AllowWebDeployerIn : ['595', 'Inbound', 'Allow', 'Tcp', 'Web', 'asg', 'asg-deployer', 'asg', 'asg-ondemand']
// Outbound
AllowCycleWebOut : ['330', 'Outbound', 'Allow', 'Tcp', 'Web', 'asg', 'asg-ondemand', 'asg', 'asg-cyclecloud']
//AllowSlurmComputeOut : ['385', 'Outbound', 'Allow', '*', 'Slurmd', 'asg', 'asg-ondemand', 'subnet', 'compute']
AllowComputeNoVncOut : ['550', 'Outbound', 'Allow', 'Tcp', 'NoVnc', 'subnet', 'compute', 'asg', 'asg-ondemand']
AllowNoVncComputeOut : ['560', 'Outbound', 'Allow', 'Tcp', 'NoVnc', 'asg', 'asg-ondemand', 'subnet', 'compute']
// AllowWebDeployerOut : ['595', 'Outbound', 'Allow', 'Tcp', 'Web', 'asg', 'asg-deployer', 'asg', 'asg-ondemand']
}
mysql: {
// Inbound
AllowMySQLIn : ['700', 'Inbound', 'Allow', 'Tcp', 'MySQL', 'asg', 'asg-mysql-client', 'subnet', 'database']
// Outbound
AllowMySQLOut : ['700', 'Outbound', 'Allow', 'Tcp', 'MySQL', 'asg', 'asg-mysql-client', 'subnet', 'database']
}
anf: {
// Inbound
AllowNfsIn : ['434', 'Inbound', 'Allow', '*', 'Nfs', 'asg', 'asg-nfs-client', 'subnet', 'netapp']
AllowNfsComputeIn : ['435', 'Inbound', 'Allow', '*', 'Nfs', 'subnet', 'compute', 'subnet', 'netapp']
// Outbound
AllowNfsOut : ['440', 'Outbound', 'Allow', '*', 'Nfs', 'asg', 'asg-nfs-client', 'subnet', 'netapp']
AllowNfsComputeOut : ['450', 'Outbound', 'Allow', '*', 'Nfs', 'subnet', 'compute', 'subnet', 'netapp']
}
ad_anf: {
// Inbound
AllowAdServerNetappTcpIn : ['300', 'Inbound', 'Allow', 'Tcp', 'DomainControlerTcp', 'subnet', 'netapp', nsgTargetForDC.type, nsgTargetForDC.target]
AllowAdServerNetappUdpIn : ['310', 'Inbound', 'Allow', 'Udp', 'DomainControlerUdp', 'subnet', 'netapp', nsgTargetForDC.type, nsgTargetForDC.target]
// Outbound
AllowAdServerNetappTcpOut : ['280', 'Outbound', 'Allow', 'Tcp', 'DomainControlerTcp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'netapp']
AllowAdServerNetappUdpOut : ['290', 'Outbound', 'Allow', 'Udp', 'DomainControlerUdp', nsgTargetForDC.type, nsgTargetForDC.target, 'subnet', 'netapp']
}
lustre: {
// Inbound
AllowLustreClientIn : ['410', 'Inbound', 'Allow', 'Tcp', 'Lustre', 'asg', 'asg-lustre-client', 'subnet', 'lustre']
AllowLustreClientComputeIn : ['420', 'Inbound', 'Allow', 'Tcp', 'Lustre', 'subnet', 'compute', 'subnet', 'lustre']
AllowLustreSubnetAnyInbound : ['430', 'Inbound', 'Allow', '*', 'All', 'subnet', 'lustre', 'subnet', 'lustre']
// Outbound
AllowAzureCloudServiceAccess: ['400', 'Outbound', 'Allow', '*', 'All', 'tag', 'VirtualNetwork', 'tag', 'AzureCloud']
AllowLustreClientOut : ['410', 'Outbound', 'Allow', 'Tcp', 'Lustre', 'asg', 'asg-lustre-client', 'subnet', 'lustre']
AllowLustreClientComputeOut : ['420', 'Outbound', 'Allow', 'Tcp', 'Lustre', 'subnet', 'compute', 'subnet', 'lustre']
AllowLustreSubnetAnyOutbound: ['430', 'Outbound', 'Allow', '*', 'All', 'subnet', 'lustre', 'subnet', 'lustre']
}
bastion: {
AllowBastionIn : ['530', 'Inbound' , 'Allow', 'Tcp', 'Bastion', 'subnet', 'bastion', 'tag', 'VirtualNetwork']
AllowBastionOut : ['531', 'Outbound', 'Allow', 'Tcp', 'Bastion', 'subnet', 'bastion', 'tag', 'VirtualNetwork']
}
gateway: {
AllowInternalWebUsersIn : ['540', 'Inbound', 'Allow', 'Tcp', 'Web', 'subnet', 'gateway', 'asg', 'asg-ondemand']
}
grafana: {
// Telegraf / Grafana
// Inbound
AllowTelegrafIn : ['490', 'Inbound', 'Allow', 'Tcp', 'Telegraf', 'asg', 'asg-telegraf', 'asg', 'asg-grafana']
AllowComputeTelegrafIn : ['500', 'Inbound', 'Allow', 'Tcp', 'Telegraf', 'subnet', 'compute', 'asg', 'asg-grafana']
AllowGrafanaIn : ['510', 'Inbound', 'Allow', 'Tcp', 'Grafana', 'asg', 'asg-ondemand', 'asg', 'asg-grafana']
// Outbound
AllowTelegrafOut : ['460', 'Outbound', 'Allow', 'Tcp', 'Telegraf', 'asg', 'asg-telegraf', 'asg', 'asg-grafana']
AllowComputeTelegrafOut : ['470', 'Outbound', 'Allow', 'Tcp', 'Telegraf', 'subnet', 'compute', 'asg', 'asg-grafana']
AllowGrafanaOut : ['480', 'Outbound', 'Allow', 'Tcp', 'Grafana', 'asg', 'asg-ondemand', 'asg', 'asg-grafana']
}
deployer: {
// Inbound
AllowSshFromDeployerIn : ['340', 'Inbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-deployer', 'asg', 'asg-ssh']
AllowDeployerToPackerSshIn : ['350', 'Inbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-deployer', 'subnet', 'admin']
// Outbound
AllowSshDeployerOut : ['510', 'Outbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-deployer', 'asg', 'asg-ssh']
AllowSshDeployerPackerOut : ['520', 'Outbound', 'Allow', 'Tcp', 'Ssh', 'asg', 'asg-deployer', 'subnet', 'admin']
}
}
}
var vmItems = items(config.vms)
module azhopSecrets './secrets.bicep' = if (autogenerateSecrets) {
name: 'azhopSecrets'
params: {
location: location
kvName: autogenerateSecrets ? azhopKeyvault.outputs.keyvaultName : 'foo' // trick to avoid unreferenced resource for azhopKeyvaultSecrets
adminUser: config.admin_user
dbAdminUser: config.slurm.admin_user
identityId: autogenerateSecrets ? identity.id : '' // trick to avoid unreferenced resource for identity
}
}
resource kv 'Microsoft.KeyVault/vaults@2022-11-01' existing = if (autogenerateSecrets) {
name: azhopKeyvault.outputs.keyvaultName
}
module natgateway './natgateway.bicep' = if (config.nat_gateway.create) {
name: 'natgateway'
params: {
location: location
name: config.nat_gateway.name
}
}
var natGatewayId = config.nat_gateway.create ? natgateway.outputs.NATGatewayId : ''
var nsgRules = items(union(
config.nsg_rules.default,
(userAuth == 'ad') ? config.nsg_rules.ad : {},
(userAuth == 'ad') && config.anf.create ? config.nsg_rules.ad_anf : {},
config.public_ip ? config.nsg_rules.internet : config.nsg_rules.hub,
config.deploy_bastion ? config.nsg_rules.bastion : {},
config.deploy_gateway ? config.nsg_rules.gateway : {},
config.anf.create ? config.nsg_rules.anf : {},
config.deploy_lustre ? config.nsg_rules.lustre : {},
config.deploy_grafana ? config.nsg_rules.grafana : {},
config.deploy_ondemand ? config.nsg_rules.ondemand: {},
createDatabase ? config.nsg_rules.mysql: {},
deployDeployer ? config.nsg_rules.deployer: {}
))
module azhopNetwork './network.bicep' = {
name: 'azhopNetwork'
params: {
location: location
vnet: config.vnet
asgNames: config.asg_names
servicePorts: config.service_ports
nsgRules: nsgRules
peerings: config.vnet.peerings
natGatewayId: natGatewayId
}
}
output vnetId string = azhopNetwork.outputs.vnetId
var subnetIds = azhopNetwork.outputs.subnetIds
var asgNameToIdLookup = reduce(azhopNetwork.outputs.asgIds, {}, (cur, next) => union(cur, next))
module azhopBastion './bastion.bicep' = if (config.deploy_bastion) {
name: 'azhopBastion'
params: {
location: location
subnetId: subnetIds.bastion
}
}
module azhopVm './vm.bicep' = [ for vm in vmItems: {
name: 'azhopVm${vm.key}'
params: {
location: location
name: contains(vm.value, 'name') ? vm.value.name : vm.key
vm: vm.value
image: config.images[vm.value.image]
subnetId: subnetIds[vm.value.subnet]
adminUser: config.admin_user
adminPassword: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.adminPassword) : adminPassword
adminSshPublicKey: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.adminSshPublicKey) : adminSshPublicKey
asgIds: asgNameToIdLookup
}
}]
// Assign roles to VMs for which roles have been specified
module azhopRoleAssignements './roleAssignments.bicep' = [ for vm in vmItems: if (contains(vm.value, 'identity') && contains(vm.value.identity, 'roles')) {
name: 'azhopRoleFor${vm.key}'
params: {
name: vm.key
roles: vm.value.identity.roles
principalId: azhopVm[indexOf(map(vmItems, item => item.key), vm.key)].outputs.principalId
}
}]
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (autogenerateSecrets) {
name: 'deployScriptIdentity'
location: location
}
resource computemi 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if(createComputeMI) {
name: computeMIname
location: location
}
module kvAccessPoliciesSecrets './kv_access_policies.bicep' = if (autogenerateSecrets) {
name: 'kvAccessPoliciesSecrets'
params: {
vaultName: autogenerateSecrets ? azhopKeyvault.outputs.keyvaultName : 'foo' // trick to avoid unreferenced resource for azhopKeyvaultSecrets
secret_permissions: ['Set']
principalId: autogenerateSecrets ? identity.properties.principalId : ''
}
}
module azhopKeyvault './keyvault.bicep' = {
name: 'azhopKeyvault'
params: {
location: location
kvName: config.key_vault_name
subnetId: subnetIds.admin
keyvaultReaderOids: config.keyvault_readers
lockDownNetwork: config.lock_down_network.enforce
allowableIps: config.lock_down_network.grant_access_from
keyvaultOwnerId: loggedUserObjectId
}
}
module kvAccessPolicies './kv_access_policies.bicep' = [ for vm in vmItems: if (contains(vm.value, 'identity') && contains(vm.value.identity, 'keyvault')) {
name: 'kvAccessPolicies${vm.key}'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
secret_permissions: contains(vm.value.identity.keyvault, 'secret_permissions') ? vm.value.identity.keyvault.secret_permissions : []
principalId: azhopVm[indexOf(map(vmItems, item => item.key), vm.key)].outputs.principalId
}
}]
module kvSecretAdminPassword './kv_secrets.bicep' = if (!autogenerateSecrets) {
name: 'kvSecrets-admin-password'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.admin_user}-password'
value: adminPassword
}
}
module kvSecretAdminPubKey './kv_secrets.bicep' = if (!autogenerateSecrets) {
name: 'kvSecrets-admin-pubkey'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.admin_user}-pubkey'
value: adminSshPublicKey
}
}
module kvSecretAdminPrivKey './kv_secrets.bicep' = if (!autogenerateSecrets) {
name: 'kvSecrets-admin-privkey'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.admin_user}-privkey'
value: adminSshPrivateKey
}
}
module kvSecretDBPassword './kv_secrets.bicep' = if (!autogenerateSecrets && createDatabase) {
name: 'kvSecrets-db-password'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.slurm.admin_user}-password'
value: databaseAdminPassword
}
}
// Domain join password when deploying AD will be stored in the keyvault
module kvSecretDomainJoin './kv_secrets.bicep' = if (createAD) {
name: 'kvSecrets-domain-join'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.domain.domain_join_user.username}-password'
value: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.adminPassword) : adminPassword
}
}
// Domain join password when using an existing AD will be retrieved from the keyvault specified in config and stored in our KV
resource domainJoinUserKV 'Microsoft.KeyVault/vaults@2021-10-01' existing = if (useExistingAD) {
name: '${config.domain.domain_join_user.password_key_vault_name}'
scope: resourceGroup(config.domain.domain_join_user.password_key_vault_resource_group_name)
}
module kvSecretExistingDomainJoin './kv_secrets.bicep' = if (useExistingAD) {
name: 'kvSecrets-existing-domain-join'
params: {
vaultName: azhopKeyvault.outputs.keyvaultName
name: '${config.domain.domain_join_user.username}-password'
value: domainJoinUserKV.getSecret(config.domain.domain_join_user.password_key_vault_secret_name)
}
}
module azhopStorage './storage.bicep' = {
name: 'azhopStorage'
params:{
location: location
saName: config.storage_account_name
lockDownNetwork: config.lock_down_network.enforce
allowableIps: config.lock_down_network.grant_access_from
subnetIds: [ subnetIds.admin, subnetIds.compute ]
}
}
module azhopSig './sig.bicep' = if (config.deploy_sig) {
name: 'azhopSig'
params: {
location: location
resourcePostfix: resourcePostfix
}
}
// module azhopMariaDB './mariadb.bicep' = if (createDatabase) {
// name: 'azhopMariaDB'
// params: {
// location: location
// mariaDbName: config.mariadb_name
// adminUser: config.slurm.admin_user
// adminPassword: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.databaseAdminPassword) : databaseAdminPassword
// adminSubnetId: subnetIds.admin
// vnetId: azhopNetwork.outputs.vnetId
// sslEnforcement: true
// }
// }
module mySQL './mysql.bicep' = if (createDatabase) {
name: 'mySQLDB'
params: {
location: location
Name: config.db_name
adminUser: config.slurm.admin_user
adminPassword: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.databaseAdminPassword) : databaseAdminPassword
subnetId: subnetIds.database
}
}
module azhopTelemetry './telemetry.bicep' = {
name: 'azhopTelemetry'
}
module azhopVpnGateway './vpngateway.bicep' = if (config.deploy_gateway) {
name: 'azhopVpnGateway'
params: {
location: location
subnetId: subnetIds.gateway
}
}
module azhopAmlfs './amlfs.bicep' = if (deployLustre) {
name: 'azhopAmlfs'
params: {
location: location
name: 'amlfs${resourcePostfix}'
subnetId: subnetIds.lustre
sku: config.lustre.sku
capacity: config.lustre.capacity
}
}
module azhopAnf './anf.bicep' = if (config.anf.create) {
name: 'azhopAnf'
params: {
location: location
resourcePostfix: resourcePostfix
dualProtocol: config.anf.dual_protocol
subnetId: subnetIds.netapp
adUser: config.admin_user
adPassword: autogenerateSecrets ? kv.getSecret(azhopSecrets.outputs.secrets.adminPassword) : adminPassword
adDns: adIp
serviceLevel: config.anf.service_level
sizeGB: config.anf.size_gb
}
}
module azhopNfsFiles './nfsfiles.bicep' = if (config.azurefiles.create ) {
name: 'azhopNfsFiles'
params: {
location: location
resourcePostfix: resourcePostfix
allowedSubnetIds: [ subnetIds.admin, subnetIds.compute, subnetIds.frontend ]
sizeGB: config.azurefiles.size_gb
}
}
module azhopPrivateZone './privatezone.bicep' = if (createAD || useExistingAD || config.private_dns.create) {
name: 'azhopPrivateZone'
params: {
privateDnsZoneName: config.domain.name
vnetId: azhopNetwork.outputs.vnetId
registrationEnabled: config.private_dns.registration_enabled
}
}
// list of DC VMs. The first one will be considered the default PDC (for DNS registration)
// Trick to get the index of the DC VM in the vmItems array, to workaround a bug in bicep 0.14.85 as it throws an error when using indexOf(map(vmItems, item => item.key), 'ad2')
var adIndex = createAD ? indexOf(map(vmItems, item => item.key), 'ad') : 0
var adIp = createAD ? azhopVm[adIndex].outputs.privateIp : ''
var ad2Index = createAD && highAvailabilityForAD ? indexOf(map(vmItems, item => item.key), 'ad2') : 0
var ad2Ip = createAD ? azhopVm[ad2Index].outputs.privateIp : ''
var domain_controller_ip_addresses = useExistingAD && contains(azhopConfig, 'domain') && contains(azhopConfig.domain, 'existing_dc_details') ? azhopConfig.domain.existing_dc_details.domain_controller_ip_addresses : []
var dcIps = createAD ? (! highAvailabilityForAD ? [adIp] : [adIp, ad2Ip]) : domain_controller_ip_addresses
module azhopADRecords './privatezone_records.bicep' = if (createAD || useExistingAD) {
name: 'azhopADRecords'
params: {
privateDnsZoneName: config.domain.name
adVmNames: config.domain.domain_controlers
adVmIps: dcIps
}
}
output ccportalPrincipalId string = azhopVm[indexOf(map(vmItems, item => item.key), 'ccportal')].outputs.principalId
output keyvaultName string = azhopKeyvault.outputs.keyvaultName
// Our input file is also the deployment output
output azhopConfig object = azhopConfig
var envNameToCloudMap = {
AzureCloud: 'AZUREPUBLICCLOUD'
AzureUSGovernment: 'AZUREUSGOVERNMENT'
AzureGermanCloud: 'AZUREGERMANCLOUD'
AzureChinaCloud: 'AZURECHINACLOUD'
}
var kvSuffix = environment().suffixes.keyvaultDns
output azhopGlobalConfig object = union(
{
global_cc_storage : config.storage_account_name
compute_subnetid : '${azhopResourceGroupName}/${config.vnet.name}/${config.vnet.subnets.compute.name}'
global_config_file : '/az-hop/config.yml'
ad_join_user : config.domain.domain_join_user.username
domain_name : config.domain.name
ldap_server : '${config.domain.ldap_server}.${config.domain.name}'
homedir_mountpoint : config.homedir_mountpoint
ondemand_fqdn : deployOnDemand ? (config.public_ip ? azhopVm[indexOf(map(vmItems, item => item.key), 'ondemand')].outputs.fqdn : azhopVm[indexOf(map(vmItems, item => item.key), 'ondemand')].outputs.privateIp) : ''
ansible_ssh_private_key_file : '${config.admin_user}_id_rsa'
subscription_id : subscription().subscriptionId
tenant_id : subscription().tenantId
key_vault : config.key_vault_name
sig_name : (config.deploy_sig) ? 'azhop_${resourcePostfix}' : ''
lustre_hsm_storage_account : config.storage_account_name
lustre_hsm_storage_container : 'lustre'
database_fqdn : createDatabase ? mySQL.outputs.fqdn : ''
database_user : config.slurm.admin_user
azure_environment : envNameToCloudMap[environment().name]
key_vault_suffix : substring(kvSuffix, 1, length(kvSuffix) - 1) // vault.azure.net - remove leading dot from env
blob_storage_suffix : 'blob.${environment().suffixes.storage}' // blob.core.windows.net
jumpbox_ssh_port : incomingSSHPort
},
createComputeMI ? {
compute_mi_id : resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', computemi.name)
}: {},
!empty(existingComputeMIrg) ? {
compute_mi_id : resourceId(existingComputeMIrg,'Microsoft.ManagedIdentity/userAssignedIdentities', computeMIname)
}: {},
config.homedir_type == 'anf' ? {
anf_home_ip : azhopAnf.outputs.nfs_home_ip
anf_home_path : azhopAnf.outputs.nfs_home_path
anf_home_opts : azhopAnf.outputs.nfs_home_opts
} : {},
config.homedir_type == 'azurefiles' ? {
anf_home_ip : azhopNfsFiles.outputs.nfs_home_ip
anf_home_path : azhopNfsFiles.outputs.nfs_home_path
anf_home_opts : azhopNfsFiles.outputs.nfs_home_opts
} : {},
config.homedir_type == 'existing' ? {
anf_home_ip : azhopConfig.mounts.home.server
anf_home_path : azhopConfig.mounts.home.export
anf_home_opts : azhopConfig.mounts.home.options
} : {},
deployLustre ? {
lustre_mgs : azhopAmlfs.outputs.lustre_mgs
} : {}
)
var sshTunelIp = deployJumpbox ? ( config.public_ip ? azhopVm[indexOf(map(vmItems, item => item.key), 'jumpbox')].outputs.publicIp : azhopVm[indexOf(map(vmItems, item => item.key), 'jumpbox')].outputs.privateIp ) : ''
output azhopInventory object = {
all: {
hosts: union (
{
localhost: {
psrp_ssh_proxy: sshTunelIp
}
scheduler: {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'scheduler')].outputs.privateIp
}
ccportal: {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'ccportal')].outputs.privateIp
}
},
config.deploy_ondemand ? {
ondemand: {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'ondemand')].outputs.privateIp
}
} : {},
indexOf(map(vmItems, item => item.key), 'ad') >= 0 ? {
ad: {
ansible_host: adIp
ansible_connection: 'psrp'
ansible_psrp_protocol: 'http'
ansible_user: config.admin_user
ansible_password: '__ADMIN_PASSWORD__'
psrp_ssh_proxy: sshTunelIp
ansible_psrp_proxy: deployJumpbox ? 'socks5h://localhost:5985' : ''
}
} : {} ,
indexOf(map(vmItems, item => item.key), 'ad2') >= 0 ? {
ad2: {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'ad2')].outputs.privateIp
ansible_connection: 'psrp'
ansible_psrp_protocol: 'http'
ansible_user: config.admin_user
ansible_password: '__ADMIN_PASSWORD__'
psrp_ssh_proxy: sshTunelIp
ansible_psrp_proxy: deployJumpbox ? 'socks5h://localhost:5985' : ''
}
} : {} ,
deployJumpbox ? {
jumpbox : {
ansible_host: sshTunelIp
ansible_ssh_port: incomingSSHPort
ansible_ssh_common_args: ''
}
} : {},
deployDeployer ? {
deployer : {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'deployer')].outputs.privateIp
ansible_ssh_port: incomingSSHPort
ansible_ssh_common_args: ''
}
} : {},
config.deploy_grafana ? {
grafana: {
ansible_host: azhopVm[indexOf(map(vmItems, item => item.key), 'grafana')].outputs.privateIp
}
} : {}
)
vars: {
ansible_ssh_user: config.admin_user
ansible_ssh_common_args: deployJumpbox ? '-o ProxyCommand="ssh -i ${config.admin_user}_id_rsa -p ${incomingSSHPort} -W %h:%p ${config.admin_user}@${sshTunelIp}"' : ''
}
}
}
output azhopPackerOptions object = (config.deploy_sig) ? {
var_subscription_id: subscription().subscriptionId
var_resource_group: azhopResourceGroupName
var_location: location
var_sig_name: 'azhop_${resourcePostfix}'
var_private_virtual_network_with_public_ip: 'false'
var_virtual_network_name: config.vnet.name
var_virtual_network_subnet_name: config.vnet.subnets.compute.name
var_virtual_network_resource_group_name: azhopResourceGroupName
var_ssh_bastion_host: sshTunelIp
var_ssh_bastion_port: incomingSSHPort
var_ssh_bastion_username: config.admin_user
var_ssh_bastion_private_key_file: '../${config.admin_user}_id_rsa'
var_queue_manager: config.queue_manager
} : {}
var azhopConnectScript = format('''
#!/bin/bash
exec ssh -i {0}_id_rsa "$@"
''', config.admin_user)
var azhopSSHConnectScript = format('''
#!/bin/bash
case $1 in
cyclecloud)
echo go create tunnel to cyclecloud at https://localhost:9443/cyclecloud
ssh -i {0}_id_rsa -fN -L 9443:ccportal:9443 -p {1} {0}@{2}
;;
ad)
echo go create tunnel to ad with rdp to localhost:3390
ssh -i {0}_id_rsa -fN -L 3390:ad:3389 -p {1} {0}@{2}
;;
deployer|jumpbox)
ssh -i {0}_id_rsa -p {1} {0}@{2}
;;
*)
exec ssh -i {0}_id_rsa -o ProxyCommand="ssh -i {0}_id_rsa -p {1} -W %h:%p {0}@{2}" -o "User={0}" "$@"
;;
esac
''', config.admin_user, incomingSSHPort, sshTunelIp)
output azhopConnectScript string = deployDeployer ? azhopConnectScript : azhopSSHConnectScript
output azhopGetSecretScript string = format('''
#!/bin/bash
user=$1
# Because secret names are restricted to '^[0-9a-zA-Z-]+$' we need to remove all other characters
secret_name=$(echo $user-password | tr -dc 'a-zA-Z0-9-')
az keyvault secret show --vault-name {0} -n $secret_name --query "value" -o tsv
''', config.key_vault_name)