Azure rendering solution deployment framework

This commit is contained in:
Rick Shahid 2022-12-04 09:05:38 -08:00
Родитель e82deb1290
Коммит e3d6b5a06e
14 изменённых файлов: 403 добавлений и 308 удалений

Просмотреть файл

@ -3,16 +3,6 @@
// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#key-vault-administrator *
// ********************************************************************************************************************************************************
#######################################################
# Storage (https://learn.microsoft.com/azure/storage) #
#######################################################
storage = {
accountType = "StorageV2" # https://learn.microsoft.com/azure/storage/common/storage-account-overview
accountRedundancy = "LRS" # https://learn.microsoft.com/azure/storage/common/storage-redundancy
accountPerformance = "Standard" # https://learn.microsoft.com/azure/storage/blobs/storage-blob-performance-tiers
}
############################################################################
# Key Vault (https://learn.microsoft.com/azure/key-vault/general/overview) #
############################################################################
@ -70,6 +60,16 @@ keyVault = {
]
}
#######################################################
# Storage (https://learn.microsoft.com/azure/storage) #
#######################################################
storage = {
accountType = "StorageV2" # https://learn.microsoft.com/azure/storage/common/storage-account-overview
accountRedundancy = "LRS" # https://learn.microsoft.com/azure/storage/common/storage-redundancy
accountPerformance = "Standard" # https://learn.microsoft.com/azure/storage/blobs/storage-blob-performance-tiers
}
######################################################################
# Monitor (https://learn.microsoft.com/azure/azure-monitor/overview) #
######################################################################

Просмотреть файл

@ -99,10 +99,14 @@ variable "monitorWorkspace" {
)
}
data "http" "current" {
url = "https://api.ipify.org?format=json"
}
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "security" {
name = module.global.securityResourceGroupName
resource "azurerm_resource_group" "render" {
name = module.global.resourceGroupName
location = module.global.regionName
}
@ -112,27 +116,8 @@ resource "azurerm_resource_group" "security" {
resource "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = azurerm_resource_group.security.name
location = azurerm_resource_group.security.location
}
#######################################################
# Storage (https://learn.microsoft.com/azure/storage) #
#######################################################
resource "azurerm_storage_account" "storage" {
name = module.global.securityStorageAccountName
resource_group_name = azurerm_resource_group.security.name
location = azurerm_resource_group.security.location
account_kind = var.storage.accountType
account_replication_type = var.storage.accountRedundancy
account_tier = var.storage.accountPerformance
allow_nested_items_to_be_public = false
}
resource "azurerm_storage_container" "container" {
name = module.global.terraformStorageContainerName
storage_account_name = azurerm_storage_account.storage.name
resource_group_name = azurerm_resource_group.render.name
location = azurerm_resource_group.render.location
}
############################################################################
@ -141,8 +126,8 @@ resource "azurerm_storage_container" "container" {
resource "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = azurerm_resource_group.security.name
location = azurerm_resource_group.security.location
resource_group_name = azurerm_resource_group.render.name
location = azurerm_resource_group.render.location
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = var.keyVault.type
purge_protection_enabled = var.keyVault.enablePurgeProtection
@ -151,6 +136,13 @@ resource "azurerm_key_vault" "render" {
enabled_for_disk_encryption = var.keyVault.enableForDiskEncryption
enabled_for_template_deployment = var.keyVault.enableForTemplateDeployment
enable_rbac_authorization = true
network_acls {
bypass = "None"
default_action = "Deny"
ip_rules = [
jsondecode(data.http.current.response_body).ip
]
}
}
resource "azurerm_key_vault_secret" "secrets" {
@ -200,14 +192,38 @@ resource "azurerm_key_vault_certificate" "certificates" {
}
}
#######################################################
# Storage (https://learn.microsoft.com/azure/storage) #
#######################################################
resource "azurerm_storage_account" "storage" {
name = module.global.storageAccountName
resource_group_name = azurerm_resource_group.render.name
location = azurerm_resource_group.render.location
account_kind = var.storage.accountType
account_replication_type = var.storage.accountRedundancy
account_tier = var.storage.accountPerformance
network_rules {
default_action = "Deny"
ip_rules = [
jsondecode(data.http.current.response_body).ip
]
}
}
resource "azurerm_storage_container" "container" {
name = module.global.storageContainerName
storage_account_name = azurerm_storage_account.storage.name
}
######################################################################
# Monitor (https://learn.microsoft.com/azure/azure-monitor/overview) #
######################################################################
resource "azurerm_log_analytics_workspace" "monitor" {
name = var.monitorWorkspace.name
resource_group_name = azurerm_resource_group.security.name
location = azurerm_resource_group.security.location
resource_group_name = azurerm_resource_group.render.name
location = azurerm_resource_group.render.location
sku = var.monitorWorkspace.sku
retention_in_days = var.monitorWorkspace.retentionDays
internet_ingestion_enabled = false
@ -215,12 +231,12 @@ resource "azurerm_log_analytics_workspace" "monitor" {
}
output "resourceGroupName" {
value = module.global.securityResourceGroupName
value = module.global.resourceGroupName
}
output "storage" {
value = merge(var.storage,
{ name = module.global.securityStorageAccountName }
{ name = module.global.storageAccountName }
)
}

Просмотреть файл

@ -3,6 +3,6 @@
# https://developer.hashicorp.com/terraform/language/settings/backends/azurerm #
####################################################################################
resource_group_name = "ArtistAnywhere" # Set to the same value as securityResourceGroupName in global variables.tf file
storage_account_name = "azrender0" # Set to the same value as securityStorageAccountName in global variables.tf file
container_name = "terraform" # Set to the same value as terraformStorageContainerName in global variables.tf file
resource_group_name = "ArtistAnywhere" # Set to the same value as resourceGroupName in variables.tf file
storage_account_name = "azrender0" # Set to the same value as storageAccountName in variables.tf file
container_name = "terraform" # Set to the same value as storageContainerName in variables.tf file

Просмотреть файл

@ -10,13 +10,13 @@ variable "renderManager" {
default = "Deadline" # RoyalRender (https://royalrender.de/) or Deadline
}
variable "securityResourceGroupName" {
variable "resourceGroupName" {
default = "ArtistAnywhere" # Alphanumeric, underscores, hyphens, periods and parenthesis are allowed
}
variable "securityStorageAccountName" {
variable "storageAccountName" {
default = "azrender0" # Set to a globally unique name (lowercase alphanumeric)
}
variable "terraformStorageContainerName" {
variable "storageContainerName" {
default = "terraform"
}
@ -61,14 +61,14 @@ output "renderManager" {
value = var.renderManager
}
output "securityResourceGroupName" {
value = var.securityResourceGroupName
output "resourceGroupName" {
value = var.resourceGroupName
}
output "securityStorageAccountName" {
value = var.securityStorageAccountName
output "storageAccountName" {
value = var.storageAccountName
}
output "terraformStorageContainerName" {
value = var.terraformStorageContainerName
output "storageContainerName" {
value = var.storageContainerName
}
output "managedIdentityName" {

Просмотреть файл

@ -5,7 +5,7 @@ resourceGroupName = "ArtistAnywhere.Network"
#################################################################################################
computeNetwork = {
name = "Compute"
name = "Render"
regionName = "" # Optional region override
addressSpace = ["10.1.0.0/16"]
dnsServerAddresses = []
@ -23,11 +23,17 @@ computeNetwork = {
serviceDelegation = ""
},
{
name = "Cache"
name = "Storage"
addressSpace = ["10.1.192.0/24"]
serviceEndpoints = ["Microsoft.Storage"]
serviceDelegation = ""
},
{
name = "Cache"
addressSpace = ["10.1.193.0/24"]
serviceEndpoints = ["Microsoft.Storage"]
serviceDelegation = ""
},
{
name = "GatewaySubnet"
addressSpace = ["10.1.255.0/26"]
@ -44,13 +50,14 @@ computeNetwork = {
subnetIndex = { # Make sure each index is in sync with corresponding subnet
farm = 0
workstation = 1
cache = 2
storage = 2
cache = 3
}
}
storageNetwork = {
name = "Storage" # Set name to "" to skip storage network deployment
regionName = "" # Optional region override
name = "" # Set to "" to skip storage network deployment
regionName = "" # Optional region override
addressSpace = ["10.0.0.0/16"]
dnsServerAddresses = []
subnets = [
@ -90,14 +97,6 @@ networkPeering = {
allowRemoteForwardedTraffic = true
}
##########################################################################################################################
# Network Address Translation (NAT) Gateway (https://learn.microsoft.com/azure/virtual-network/nat-gateway/nat-overview) #
##########################################################################################################################
natGateway = {
enable = false
}
############################################################################
# Private DNS (https://learn.microsoft.com/azure/dns/private-dns-overview) #
############################################################################
@ -122,6 +121,14 @@ bastion = {
enableShareableLink = false
}
##########################################################################################################################
# Network Address Translation (NAT) Gateway (https://learn.microsoft.com/azure/virtual-network/nat-gateway/nat-overview) #
##########################################################################################################################
natGateway = {
enable = false
}
###########################
# Virtual Network Gateway #
###########################
@ -142,7 +149,7 @@ vpnGateway = {
generation = "Generation2"
enableBgp = false
enableActiveActive = false
pointToSiteClient = { # https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-howto-point-to-site-resource-manager-portal
pointToSiteClient = {
addressSpace = []
certificateName = ""
certificateData = ""
@ -155,7 +162,7 @@ vpnGateway = {
vpnGatewayLocal = {
fqdn = "" # Set the fully-qualified domain name (FQDN) of your on-premises VPN gateway device
address = "" # OR set the public IP address of your on-prem VPN gateway device. Do not set both.
address = "" # or set the public IP address. Do NOT set both "fqdn" and "address" parameters
addressSpace = []
bgp = {
enable = false
@ -172,7 +179,7 @@ vpnGatewayLocal = {
expressRouteGateway = {
sku = "" # https://learn.microsoft.com/azure/expressroute/expressroute-about-virtual-network-gateways#gwsku
connection = {
circuitId = "" # Expected format is "/subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.Network/expressRouteCircuits/[circuit_name]"
circuitId = "" # Expected format = "/subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.Network/expressRouteCircuits/[circuit_name]"
authorizationKey = ""
enableFastPath = false # https://learn.microsoft.com/azure/expressroute/about-fastpath
}

Просмотреть файл

@ -46,6 +46,7 @@ variable "computeNetwork" {
{
farm = number
workstation = number
storage = number
cache = number
}
)
@ -89,14 +90,6 @@ variable "networkPeering" {
)
}
variable "natGateway" {
type = object(
{
enable = bool
}
)
}
variable "privateDns" {
type = object(
{
@ -121,6 +114,14 @@ variable "bastion" {
)
}
variable "natGateway" {
type = object(
{
enable = bool
}
)
}
variable "networkGateway" {
type = object(
{
@ -183,7 +184,7 @@ variable "expressRouteGateway" {
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "gateway_connection" {
@ -191,6 +192,11 @@ data "azurerm_key_vault_secret" "gateway_connection" {
key_vault_id = data.azurerm_key_vault.render.id
}
data "azurerm_storage_account" "render" {
name = module.global.storageAccountName
resource_group_name = module.global.resourceGroupName
}
locals {
computeNetwork = var.computeNetwork.regionName == "" ? merge(var.computeNetwork,
{ regionName = module.global.regionName }
@ -206,8 +212,12 @@ locals {
storageNetworkSubnets = [
for virtualNetworkSubnet in local.storageNetwork.subnets : merge(virtualNetworkSubnet,
{ virtualNetworkName = local.storageNetwork.name }
) if virtualNetworkSubnet.name != "GatewaySubnet"
) if virtualNetworkSubnet.name != "GatewaySubnet" && local.storageNetwork.name != ""
]
computeStorageSubnet = merge(local.computeNetwork.subnets[local.computeNetwork.subnetIndex.storage],
{ virtualNetworkName = local.computeNetwork.name }
)
storageSubnets = setunion(local.storageNetworkSubnets, [local.computeStorageSubnet])
virtualNetworks = distinct(local.storageNetwork.name == "" ? [local.computeNetwork, local.computeNetwork] : [local.computeNetwork, local.storageNetwork])
virtualNetworksSubnets = flatten([
for virtualNetwork in local.virtualNetworks : [
@ -413,72 +423,6 @@ resource "azurerm_virtual_network_peering" "network_peering_down" {
]
}
##########################################################################################################################
# Network Address Translation (NAT) Gateway (https://learn.microsoft.com/azure/virtual-network/nat-gateway/nat-overview) #
##########################################################################################################################
resource "azurerm_public_ip" "nat_gateway_address_compute" {
count = var.natGateway.enable ? 1 : 0
name = azurerm_nat_gateway.compute[0].name
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_public_ip" "nat_gateway_address_storage" {
count = var.natGateway.enable ? 1 : 0
name = azurerm_nat_gateway.storage[0].name
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_nat_gateway" "compute" {
count = var.natGateway.enable ? 1 : 0
name = "${local.computeNetwork.name}.NAT"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku_name = "Standard"
}
resource "azurerm_nat_gateway" "storage" {
count = var.natGateway.enable ? 1 : 0
name = "${local.storageNetwork.name}.NAT"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku_name = "Standard"
}
resource "azurerm_nat_gateway_public_ip_association" "compute" {
count = var.natGateway.enable ? 1 : 0
nat_gateway_id = azurerm_nat_gateway.compute[0].id
public_ip_address_id = azurerm_public_ip.nat_gateway_address_compute[0].id
}
resource "azurerm_nat_gateway_public_ip_association" "storage" {
count = var.natGateway.enable ? 1 : 0
nat_gateway_id = azurerm_nat_gateway.storage[0].id
public_ip_address_id = azurerm_public_ip.nat_gateway_address_storage[0].id
}
resource "azurerm_subnet_nat_gateway_association" "compute" {
for_each = {
for virtualNetworkSubnet in local.computeNetworkSubnets : virtualNetworkSubnet.name => virtualNetworkSubnet if var.natGateway.enable
}
nat_gateway_id = azurerm_nat_gateway.compute[0].id
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
}
resource "azurerm_subnet_nat_gateway_association" "storage" {
for_each = {
for virtualNetworkSubnet in local.storageNetworkSubnets : virtualNetworkSubnet.name => virtualNetworkSubnet if var.natGateway.enable
}
nat_gateway_id = azurerm_nat_gateway.storage[0].id
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
}
############################################################################
# Private DNS (https://learn.microsoft.com/azure/dns/private-dns-overview) #
############################################################################
@ -502,6 +446,130 @@ resource "azurerm_private_dns_zone_virtual_network_link" "network" {
]
}
###############################################################################################
# Private Endpoint (https://learn.microsoft.com/azure/private-link/private-endpoint-overview) #
###############################################################################################
resource "azurerm_private_dns_zone" "key_vault" {
name = "privatelink.vaultcore.azure.net"
resource_group_name = azurerm_resource_group.network.name
}
resource "azurerm_private_dns_zone" "storage_blob" {
name = "privatelink.blob.core.windows.net"
resource_group_name = azurerm_resource_group.network.name
}
resource "azurerm_private_dns_zone" "storage_file" {
name = "privatelink.file.core.windows.net"
resource_group_name = azurerm_resource_group.network.name
}
resource "azurerm_private_dns_zone_virtual_network_link" "key_vault" {
name = "${local.computeNetwork.name}.vault"
resource_group_name = azurerm_resource_group.network.name
private_dns_zone_name = azurerm_private_dns_zone.key_vault.name
virtual_network_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${local.computeNetwork.name}"
}
resource "azurerm_private_dns_zone_virtual_network_link" "storage_blob" {
for_each = {
for virtualNetwork in local.virtualNetworks : virtualNetwork.name => virtualNetwork
}
name = "${each.value.name}.blob"
resource_group_name = azurerm_resource_group.network.name
private_dns_zone_name = azurerm_private_dns_zone.storage_blob.name
virtual_network_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.name}"
}
resource "azurerm_private_dns_zone_virtual_network_link" "storage_file" {
for_each = {
for virtualNetwork in local.virtualNetworks : virtualNetwork.name => virtualNetwork
}
name = "${each.value.name}.file"
resource_group_name = azurerm_resource_group.network.name
private_dns_zone_name = azurerm_private_dns_zone.storage_file.name
virtual_network_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.name}"
}
resource "azurerm_private_endpoint" "key_vault" {
name = "${data.azurerm_storage_account.render.name}.vault"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
subnet_id = "${azurerm_private_dns_zone_virtual_network_link.key_vault.virtual_network_id}/subnets/${local.computeNetwork.subnets[local.computeNetwork.subnetIndex.storage].name}"
private_service_connection {
name = data.azurerm_key_vault.render.name
private_connection_resource_id = data.azurerm_key_vault.render.id
is_manual_connection = false
subresource_names = [
"vault"
]
}
private_dns_zone_group {
name = data.azurerm_key_vault.render.name
private_dns_zone_ids = [
azurerm_private_dns_zone.key_vault.id
]
}
depends_on = [
azurerm_subnet.network
]
}
resource "azurerm_private_endpoint" "storage_blob" {
for_each = {
for storageSubnet in local.storageSubnets : storageSubnet.name => storageSubnet
}
name = "${data.azurerm_storage_account.render.name}.blob"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
private_service_connection {
name = data.azurerm_storage_account.render.name
private_connection_resource_id = data.azurerm_storage_account.render.id
is_manual_connection = false
subresource_names = [
"blob"
]
}
private_dns_zone_group {
name = data.azurerm_storage_account.render.name
private_dns_zone_ids = [
azurerm_private_dns_zone.storage_blob.id
]
}
depends_on = [
azurerm_private_endpoint.key_vault
]
}
resource "azurerm_private_endpoint" "storage_file" {
for_each = {
for storageSubnet in local.storageSubnets : storageSubnet.name => storageSubnet
}
name = "${data.azurerm_storage_account.render.name}.file"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
private_service_connection {
name = data.azurerm_storage_account.render.name
private_connection_resource_id = data.azurerm_storage_account.render.id
is_manual_connection = false
subresource_names = [
"file"
]
}
private_dns_zone_group {
name = data.azurerm_storage_account.render.name
private_dns_zone_ids = [
azurerm_private_dns_zone.storage_file.id
]
}
depends_on = [
azurerm_private_endpoint.storage_blob
]
}
########################################################################
# Bastion (https://learn.microsoft.com/azure/bastion/bastion-overview) #
########################################################################
@ -645,9 +713,75 @@ resource "azurerm_bastion_host" "compute" {
]
}
#######################################
# Virtual Network Gateway (Public IP) #
#######################################
##########################################################################################################################
# Network Address Translation (NAT) Gateway (https://learn.microsoft.com/azure/virtual-network/nat-gateway/nat-overview) #
##########################################################################################################################
resource "azurerm_public_ip" "nat_gateway_address_compute" {
count = var.natGateway.enable ? 1 : 0
name = azurerm_nat_gateway.compute[0].name
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_public_ip" "nat_gateway_address_storage" {
count = local.storageNetwork.name != "" && var.natGateway.enable ? 1 : 0
name = azurerm_nat_gateway.storage[0].name
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_nat_gateway" "compute" {
count = var.natGateway.enable ? 1 : 0
name = "${local.computeNetwork.name}.Gateway"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku_name = "Standard"
}
resource "azurerm_nat_gateway" "storage" {
count = local.storageNetwork.name != "" && var.natGateway.enable ? 1 : 0
name = "${local.storageNetwork.name}.Gateway"
resource_group_name = azurerm_resource_group.network.name
location = azurerm_resource_group.network.location
sku_name = "Standard"
}
resource "azurerm_nat_gateway_public_ip_association" "compute" {
count = var.natGateway.enable ? 1 : 0
nat_gateway_id = azurerm_nat_gateway.compute[0].id
public_ip_address_id = azurerm_public_ip.nat_gateway_address_compute[0].id
}
resource "azurerm_nat_gateway_public_ip_association" "storage" {
count = local.storageNetwork.name != "" && var.natGateway.enable ? 1 : 0
nat_gateway_id = azurerm_nat_gateway.storage[0].id
public_ip_address_id = azurerm_public_ip.nat_gateway_address_storage[0].id
}
resource "azurerm_subnet_nat_gateway_association" "compute" {
for_each = {
for virtualNetworkSubnet in local.computeNetworkSubnets : virtualNetworkSubnet.name => virtualNetworkSubnet if var.natGateway.enable
}
nat_gateway_id = azurerm_nat_gateway.compute[0].id
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
}
resource "azurerm_subnet_nat_gateway_association" "storage" {
for_each = {
for virtualNetworkSubnet in local.storageNetworkSubnets : virtualNetworkSubnet.name => virtualNetworkSubnet if var.natGateway.enable
}
nat_gateway_id = azurerm_nat_gateway.storage[0].id
subnet_id = "${azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${each.value.virtualNetworkName}/subnets/${each.value.name}"
}
#################################################
# Virtual Network Gateway (Public IP Addresses) #
#################################################
resource "azurerm_public_ip" "vnet_gateway_address1" {
for_each = {

Просмотреть файл

@ -14,9 +14,6 @@ storageAccounts = [
enableBlobNfsV3 = true # https://learn.microsoft.com/azure/storage/blobs/network-file-system-protocol-support
enableLargeFileShare = false # https://learn.microsoft.com/azure/storage/files/storage-how-to-create-file-share#advanced
enableSampleDataLoad = false
privateEndpointTypes = [ # https://learn.microsoft.com/azure/storage/common/storage-private-endpoints
"blob"
]
blobContainers = [ # https://learn.microsoft.com/azure/storage/blobs/storage-blobs-introduction
{
name = "show"
@ -37,9 +34,6 @@ storageAccounts = [
enableBlobNfsV3 = false # https://learn.microsoft.com/azure/storage/blobs/network-file-system-protocol-support
enableLargeFileShare = true # https://learn.microsoft.com/azure/storage/files/storage-how-to-create-file-share#advanced
enableSampleDataLoad = false
privateEndpointTypes = [ # https://learn.microsoft.com/azure/storage/common/storage-private-endpoints
"file"
]
blobContainers = [ # https://learn.microsoft.com/azure/storage/blobs/storage-blobs-introduction
]
fileShares = [ # https://learn.microsoft.com/azure/storage/files/storage-files-introduction

Просмотреть файл

@ -47,7 +47,6 @@ variable "storageAccounts" {
enableBlobNfsV3 = bool
enableLargeFileShare = bool
enableSampleDataLoad = bool
privateEndpointTypes = list(string)
blobContainers = list(object(
{
name = string
@ -195,9 +194,13 @@ variable "storageNetwork" {
)
}
data "http" "current" {
url = "https://api.ipify.org?format=json"
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "admin_password" {
@ -208,65 +211,58 @@ data "azurerm_key_vault_secret" "admin_password" {
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
data "azurerm_resource_group" "network" {
name = data.azurerm_virtual_network.storage.resource_group_name
name = data.azurerm_virtual_network.compute.resource_group_name
}
data "azurerm_virtual_network" "compute" {
name = !local.stateExistsNetwork ? var.storageNetwork.name : data.terraform_remote_state.network.outputs.computeNetwork.name
resource_group_name = !local.stateExistsNetwork ? var.storageNetwork.resourceGroupName : data.terraform_remote_state.network.outputs.resourceGroupName
}
data "azurerm_virtual_network" "storage" {
count = (!local.stateExistsNetwork && var.storageNetwork.name != "") || (local.stateExistsNetwork && data.terraform_remote_state.network.outputs.storageNetwork.name != "") ? 1 : 0
name = !local.stateExistsNetwork ? var.storageNetwork.name : data.terraform_remote_state.network.outputs.storageNetwork.name
resource_group_name = !local.stateExistsNetwork ? var.storageNetwork.resourceGroupName : data.terraform_remote_state.network.outputs.resourceGroupName
}
data "azurerm_subnet" "compute_storage" {
name = !local.stateExistsNetwork ? var.storageNetwork.subnetNamePrimary : data.terraform_remote_state.network.outputs.computeNetwork.subnets[data.terraform_remote_state.network.outputs.computeNetwork.subnetIndex.storage].name
resource_group_name = data.azurerm_virtual_network.compute.resource_group_name
virtual_network_name = data.azurerm_virtual_network.compute.name
}
data "azurerm_subnet" "storage_primary" {
count = (!local.stateExistsNetwork && var.storageNetwork.name != "") || (local.stateExistsNetwork && data.terraform_remote_state.network.outputs.storageNetwork.name != "") ? 1 : 0
name = !local.stateExistsNetwork ? var.storageNetwork.subnetNamePrimary : data.terraform_remote_state.network.outputs.storageNetwork.subnets[data.terraform_remote_state.network.outputs.storageNetwork.subnetIndex.primary].name
resource_group_name = data.azurerm_virtual_network.storage.resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage.name
resource_group_name = data.azurerm_virtual_network.storage[0].resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage[0].name
}
data "azurerm_subnet" "storage_secondary" {
count = (!local.stateExistsNetwork && var.storageNetwork.name != "") || (local.stateExistsNetwork && data.terraform_remote_state.network.outputs.storageNetwork.name != "") ? 1 : 0
name = !local.stateExistsNetwork ? var.storageNetwork.subnetNameSecondary : data.terraform_remote_state.network.outputs.storageNetwork.subnets[data.terraform_remote_state.network.outputs.storageNetwork.subnetIndex.secondary].name
resource_group_name = data.azurerm_virtual_network.storage.resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage.name
resource_group_name = data.azurerm_virtual_network.storage[0].resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage[0].name
}
data "azurerm_subnet" "storage_netapp" {
count = var.netAppAccount.name != "" ? 1 : 0
count = (!local.stateExistsNetwork && var.storageNetwork.name != "") || (local.stateExistsNetwork && data.terraform_remote_state.network.outputs.storageNetwork.name != "") ? 1 : 0
name = !local.stateExistsNetwork ? var.storageNetwork.subnetNamePrimary : data.terraform_remote_state.network.outputs.storageNetwork.subnets[data.terraform_remote_state.network.outputs.storageNetwork.subnetIndex.netAppFiles].name
resource_group_name = data.azurerm_virtual_network.storage.resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage.name
}
data "http" "current_host" {
url = "https://api.ipify.org?format=json"
resource_group_name = data.azurerm_virtual_network.storage[0].resource_group_name
virtual_network_name = data.azurerm_virtual_network.storage[0].name
}
locals {
stateExistsNetwork = try(length(data.terraform_remote_state.network.outputs) >= 0, false)
serviceEndpointSubnets = !local.stateExistsNetwork ? var.storageNetwork.serviceEndpointSubnets : data.terraform_remote_state.network.outputs.storageEndpointSubnets
privateDnsZones = distinct(flatten([
for storageAccount in var.storageAccounts : [
for privateEndpointType in storageAccount.privateEndpointTypes : {
name = "privatelink.${privateEndpointType}.core.windows.net"
}
]
]))
privateEndpoints = flatten([
for storageAccount in var.storageAccounts : [
for privateEndpointType in storageAccount.privateEndpointTypes : {
type = privateEndpointType
privateDnsZoneName = "privatelink.${privateEndpointType}.core.windows.net"
storageAccountName = storageAccount.name
storageAccountId = "${azurerm_resource_group.storage.id}/providers/Microsoft.Storage/storageAccounts/${storageAccount.name}"
}
]
])
blobContainers = flatten([
for storageAccount in var.storageAccounts : [
for blobContainer in storageAccount.blobContainers : {
@ -360,7 +356,7 @@ locals {
"networks": {
"eth0": {
"cluster_ips": [
"@METADATA_HOST_IP@/${reverse(split("/", data.azurerm_subnet.storage_primary.address_prefixes[0]))[0]}"
"@METADATA_HOST_IP@/${reverse(split("/", try(data.azurerm_subnet.storage_primary[0].address_prefixes[0], data.azurerm_subnet.compute_storage.address_prefixes[0])))[0]}"
]
},
"eth1": {
@ -374,7 +370,7 @@ locals {
"domainname": local.hammerspaceDomainName
"metadata": {
"ips": [
"@METADATA_HOST_IP@/${reverse(split("/", data.azurerm_subnet.storage_primary.address_prefixes[0]))[0]}"
"@METADATA_HOST_IP@/${reverse(split("/", try(data.azurerm_subnet.storage_primary[0].address_prefixes[0], data.azurerm_subnet.compute_storage.address_prefixes[0])))[0]}"
]
}
},
@ -399,26 +395,23 @@ locals {
resource "azurerm_resource_group" "storage" {
name = var.resourceGroupName
location = data.azurerm_virtual_network.storage.location
location = try(data.azurerm_virtual_network.storage[0].location, data.azurerm_virtual_network.compute.location)
}
resource "azurerm_storage_account" "storage" {
for_each = {
for storageAccount in var.storageAccounts : storageAccount.name => storageAccount
}
name = each.value.name
resource_group_name = azurerm_resource_group.storage.name
location = azurerm_resource_group.storage.location
account_kind = each.value.type
account_tier = each.value.tier
account_replication_type = each.value.redundancy
enable_https_traffic_only = each.value.enableHttpsOnly
is_hns_enabled = each.value.enableBlobNfsV3
nfsv3_enabled = each.value.enableBlobNfsV3
large_file_share_enabled = each.value.enableLargeFileShare ? true : null
public_network_access_enabled = length(local.serviceEndpointSubnets) > 0
allow_nested_items_to_be_public = false
default_to_oauth_authentication = true
name = each.value.name
resource_group_name = azurerm_resource_group.storage.name
location = azurerm_resource_group.storage.location
account_kind = each.value.type
account_tier = each.value.tier
account_replication_type = each.value.redundancy
enable_https_traffic_only = each.value.enableHttpsOnly
is_hns_enabled = each.value.enableBlobNfsV3
nfsv3_enabled = each.value.enableBlobNfsV3
large_file_share_enabled = each.value.enableLargeFileShare ? true : null
network_rules {
default_action = "Deny"
virtual_network_subnet_ids = [
@ -426,60 +419,11 @@ resource "azurerm_storage_account" "storage" {
"${data.azurerm_resource_group.network.id}/providers/Microsoft.Network/virtualNetworks/${serviceEndpointSubnet.virtualNetworkName}/subnets/${serviceEndpointSubnet.name}"
]
ip_rules = each.value.enableSampleDataLoad ? [
jsondecode(data.http.current_host.response_body).ip
jsondecode(data.http.current.response_body).ip
] : []
}
}
resource "azurerm_private_dns_zone" "zones" {
for_each = {
for privateDnsZone in local.privateDnsZones : privateDnsZone.name => privateDnsZone
}
name = each.value.name
resource_group_name = azurerm_resource_group.storage.name
}
resource "azurerm_private_dns_zone_virtual_network_link" "zone_links" {
for_each = {
for privateDnsZone in local.privateDnsZones : privateDnsZone.name => privateDnsZone
}
name = data.azurerm_virtual_network.storage.name
resource_group_name = azurerm_resource_group.storage.name
private_dns_zone_name = each.value.name
virtual_network_id = data.azurerm_virtual_network.storage.id
depends_on = [
azurerm_private_dns_zone.zones
]
}
resource "azurerm_private_endpoint" "storage" {
for_each = {
for privateEndpoint in local.privateEndpoints : "${privateEndpoint.storageAccountName}.${privateEndpoint.type}" => privateEndpoint
}
name = "${each.value.storageAccountName}.${each.value.type}"
resource_group_name = azurerm_resource_group.storage.name
location = azurerm_resource_group.storage.location
subnet_id = data.azurerm_subnet.storage_primary.id
private_service_connection {
name = each.value.storageAccountName
private_connection_resource_id = each.value.storageAccountId
is_manual_connection = false
subresource_names = [
each.value.type
]
}
private_dns_zone_group {
name = each.value.storageAccountName
private_dns_zone_ids = [
"${azurerm_resource_group.storage.id}/providers/Microsoft.Network/privateDnsZones/${each.value.privateDnsZoneName}"
]
}
depends_on = [
azurerm_storage_account.storage,
azurerm_private_dns_zone_virtual_network_link.zone_links
]
}
resource "time_sleep" "storage_data" {
for_each = {
for storageAccount in var.storageAccounts : storageAccount.name => storageAccount if storageAccount.enableSampleDataLoad
@ -536,7 +480,7 @@ resource "azurerm_storage_share" "shares" {
resource "azurerm_resource_group" "netapp_files" {
count = var.netAppAccount.name != "" ? 1 : 0
name = "${var.resourceGroupName}.NetAppFiles"
location = data.azurerm_virtual_network.storage.location
location = data.azurerm_virtual_network.storage[0].location
}
resource "azurerm_netapp_account" "storage" {
@ -598,7 +542,7 @@ resource "azurerm_netapp_volume" "storage" {
resource "azurerm_resource_group" "hammerspace" {
count = var.hammerspace.namePrefix != "" ? 1 : 0
name = "${var.resourceGroupName}.Hammerspace"
location = data.azurerm_virtual_network.storage.location
location = data.azurerm_virtual_network.storage[0].location
}
resource "azurerm_proximity_placement_group" "storage" {
@ -633,7 +577,7 @@ resource "azurerm_network_interface" "storage_primary" {
location = azurerm_resource_group.hammerspace[0].location
ip_configuration {
name = "ipConfig"
subnet_id = data.azurerm_subnet.storage_primary.id
subnet_id = try(data.azurerm_subnet.storage_primary[0].id, data.azurerm_subnet.compute_storage.id)
private_ip_address_allocation = "Dynamic"
}
enable_accelerated_networking = each.value.enableAcceleratedNetworking
@ -648,7 +592,7 @@ resource "azurerm_network_interface" "storage_secondary" {
location = azurerm_resource_group.hammerspace[0].location
ip_configuration {
name = "ipConfig"
subnet_id = data.azurerm_subnet.storage_secondary.id
subnet_id = try(data.azurerm_subnet.storage_secondary[0].id, data.azurerm_subnet.compute_storage.id)
private_ip_address_allocation = "Dynamic"
}
enable_accelerated_networking = each.value.enableAcceleratedNetworking
@ -824,7 +768,7 @@ resource "azurerm_lb" "storage" {
sku = "Standard"
frontend_ip_configuration {
name = "ipConfigFrontend"
subnet_id = data.azurerm_subnet.storage_primary.id
subnet_id = try(data.azurerm_subnet.storage_primary[0].id, data.azurerm_subnet.compute_storage.id)
}
}

Просмотреть файл

@ -156,12 +156,12 @@ data "azurerm_client_config" "current" {}
data "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_key" "cache_encryption" {
@ -172,15 +172,15 @@ data "azurerm_key_vault_key" "cache_encryption" {
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
data "azurerm_resource_group" "identity" {
name = module.global.securityResourceGroupName
data "azurerm_resource_group" "render" {
name = module.global.resourceGroupName
}
data "azurerm_resource_group" "network" {
@ -309,7 +309,7 @@ data "azurerm_key_vault_secret" "admin_password" {
resource "azurerm_role_assignment" "identity" {
role_definition_name = "Managed Identity Operator" # https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#managed-identity-operator
principal_id = data.azurerm_user_assigned_identity.render.principal_id
scope = data.azurerm_resource_group.identity.id
scope = data.azurerm_resource_group.render.id
}
resource "azurerm_role_assignment" "network" {

Просмотреть файл

@ -86,12 +86,12 @@ variable "computeNetwork" {
data "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "admin_username" {
@ -107,9 +107,9 @@ data "azurerm_key_vault_secret" "admin_password" {
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
@ -124,8 +124,8 @@ data "azurerm_virtual_network" "compute" {
}
data "azurerm_storage_account" "storage" {
name = module.global.securityStorageAccountName
resource_group_name = module.global.securityResourceGroupName
name = module.global.storageAccountName
resource_group_name = module.global.resourceGroupName
}
locals {
@ -229,7 +229,7 @@ resource "azurerm_resource_group_template_deployment" "image_builder" {
value = module.global.managedIdentityName
}
"managedIdentityResourceGroupName" = {
value = module.global.securityResourceGroupName
value = module.global.resourceGroupName
}
"imageGalleryName" = {
value = var.imageGallery.name

Просмотреть файл

@ -126,12 +126,12 @@ data "azurerm_client_config" "current" {}
data "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "admin_username" {
@ -146,15 +146,15 @@ data "azurerm_key_vault_secret" "admin_password" {
data "azurerm_log_analytics_workspace" "monitor" {
name = module.global.monitorWorkspaceName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
@ -162,9 +162,9 @@ data "terraform_remote_state" "network" {
data "terraform_remote_state" "image" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "4.image.builder"
}
}

Просмотреть файл

@ -7,7 +7,7 @@ terraform {
}
}
backend "azurerm" {
key = "5.render.farm"
key = "6.render.farm"
}
}
@ -143,12 +143,12 @@ variable "computeNetwork" {
data "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "admin_password" {
@ -158,15 +158,15 @@ data "azurerm_key_vault_secret" "admin_password" {
data "azurerm_log_analytics_workspace" "monitor" {
name = module.global.monitorWorkspaceName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
@ -174,9 +174,9 @@ data "terraform_remote_state" "network" {
data "terraform_remote_state" "image" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "4.image.builder"
}
}

Просмотреть файл

@ -93,12 +93,12 @@ variable "computeNetwork" {
data "azurerm_user_assigned_identity" "render" {
name = module.global.managedIdentityName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault" "render" {
name = module.global.keyVaultName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "azurerm_key_vault_secret" "admin_password" {
@ -108,15 +108,15 @@ data "azurerm_key_vault_secret" "admin_password" {
data "azurerm_log_analytics_workspace" "monitor" {
name = module.global.monitorWorkspaceName
resource_group_name = module.global.securityResourceGroupName
resource_group_name = module.global.resourceGroupName
}
data "terraform_remote_state" "network" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "1.network"
}
}
@ -124,9 +124,9 @@ data "terraform_remote_state" "network" {
data "terraform_remote_state" "image" {
backend = "azurerm"
config = {
resource_group_name = module.global.securityResourceGroupName
storage_account_name = module.global.securityStorageAccountName
container_name = module.global.terraformStorageContainerName
resource_group_name = module.global.resourceGroupName
storage_account_name = module.global.storageAccountName
container_name = module.global.storageContainerName
key = "4.image.builder"
}
}

Просмотреть файл

@ -4,10 +4,10 @@ Azure Artist Anywhere (AAA) is a *modular and customizable [infrastructure-as-co
https://user-images.githubusercontent.com/22285652/202864874-e48070dc-deaa-45ee-a8ed-60ff401955f0.mp4
The following *core design principles* are implemented throughout the AAA solution deployment framework.
* Integration of security best practices, including [Managed Identity](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview), [Key Vault](https://learn.microsoft.com/azure/key-vault/general/overview), [Private Endpoints](https://learn.microsoft.com/azure/private-link/private-endpoint-overview), [Network Security Groups](https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview), etc.
* Any custom or 3rd-party software (render manager, render engines, etc) in a [Compute Gallery](https://learn.microsoft.com/azure/virtual-machines/shared-image-galleries) custom image is supported.
* Clean separation of AAA module deployment configuration files (*config.auto.tfvars*) and code files (*main.tf*) via [Terraform](https://www.terraform.io).
The following *core principles* are implemented throughout the AAA solution deployment framework.
* Defense-in-depth layered security model across [Managed Identity](https://learn.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview), [Key Vault](https://learn.microsoft.com/azure/key-vault/general/overview), [Private Link](https://learn.microsoft.com/azure/private-link/private-link-overview) / [Endpoints](https://learn.microsoft.com/azure/private-link/private-endpoint-overview), [Network Security Groups](https://learn.microsoft.com/azure/virtual-network/network-security-groups-overview), etc.
* Any custom or 3rd-party software (such as a render manager, render engines, etc) in a [Compute Gallery](https://learn.microsoft.com/azure/virtual-machines/shared-image-galleries) custom image is supported.
* Clean separation of AAA module deployment configuration files (*config.auto.tfvars*) and code template files (*main.tf*) via [Terraform](https://www.terraform.io).
| **Module Name** | **Module Description** | **Module Required for<br>Burst Compute Only?** | **Module Required for<br>All Cloud Solution?<br>(Compute & Storage)** |
| - | - | - | - |
@ -15,9 +15,9 @@ The following *core design principles* are implemented throughout the AAA soluti
| [1 Network](#1-network) | Deploys [Virtual Network](https://learn.microsoft.com/azure/virtual-network/virtual-networks-overview) and [Bastion](https://learn.microsoft.com/azure/bastion/bastion-overview) with [VPN](https://learn.microsoft.com/azure/vpn-gateway/vpn-gateway-about-vpngateways) or [ExpressRoute](https://learn.microsoft.com/azure/expressroute/expressroute-about-virtual-network-gateways) hybrid networking services. | Yes, if [Virtual Network](https://learn.microsoft.com/azure/virtual-network/virtual-networks-overview) not deployed. Otherwise, No | Yes, if [Virtual Network](https://learn.microsoft.com/azure/virtual-network/virtual-networks-overview) not deployed. Otherwise, No |
| [2 Storage](#2-storage) | Deploys [Blob (NFS)](https://learn.microsoft.com/azure/storage/blobs/network-file-system-protocol-support), [Files](https://learn.microsoft.com/azure/storage/files/storage-files-introduction), [NetApp Files](https://learn.microsoft.com/azure/azure-netapp-files/azure-netapp-files-introduction) or [Hammerspace](https://azuremarketplace.microsoft.com/marketplace/apps/hammerspace.hammerspace_4_6_5) storage services. | No | Yes |
| [3 Storage Cache](#3-storage-cache) | Deploys [HPC Cache](https://learn.microsoft.com/azure/hpc-cache/hpc-cache-overview) or [Avere vFXT](https://learn.microsoft.com/azure/avere-vfxt/avere-vfxt-overview) for highly-available and scalable storage file caching. | Yes | Maybe, depends on your<br>render scale requirements |
| [4 Image Builder](#4-image-builder) | Deploys [Compute Gallery](https://learn.microsoft.com/azure/virtual-machines/shared-image-galleries) images that are built via the managed [Image Builder](https://learn.microsoft.com/azure/virtual-machines/image-builder-overview) service. | No, specify your custom *imageId* reference [here](https://github.com/Azure/Avere/blob/main/src/terraform/examples/e2e/6.render.farm/config.auto.tfvars#L10) | No, specify your custom *imageId* reference [here](https://github.com/Azure/Avere/blob/main/src/terraform/examples/e2e/6.render.farm/config.auto.tfvars#L10) |
| [5 Render Manager](#5-render-manager) | Deploys [Virtual Machines](https://learn.microsoft.com/azure/virtual-machines) for job scheduling with optional [CycleCloud](https://learn.microsoft.com/azure/cyclecloud/overview) orchestration. | No, continue to use your current render manager | No, continue to use your current render manager |
| [6 Render Farm](#6-render-farm) | Deploys [Virtual Machine Scale Sets](https://learn.microsoft.com/azure/virtual-machine-scale-sets/overview) or [Kubernetes Clusters](https://learn.microsoft.com/azure/aks/intro-kubernetes) / [Fleets](https://learn.microsoft.com/azure/kubernetes-fleet/overview) for render farms. | Yes | Yes |
| [4 Image Builder](#4-image-builder) | Deploys [Compute Gallery](https://learn.microsoft.com/azure/virtual-machines/shared-image-galleries) images that are custom built via the managed [Image Builder](https://learn.microsoft.com/azure/virtual-machines/image-builder-overview) service. | No, specify your custom *imageId* reference [here](https://github.com/Azure/Avere/blob/main/src/terraform/examples/e2e/6.render.farm/config.auto.tfvars#L10) | No, specify your custom *imageId* reference [here](https://github.com/Azure/Avere/blob/main/src/terraform/examples/e2e/6.render.farm/config.auto.tfvars#L10) |
| [5 Render Manager](#5-render-manager) | Deploys [Virtual Machines](https://learn.microsoft.com/azure/virtual-machines) for job scheduling with optional [CycleCloud](https://learn.microsoft.com/azure/cyclecloud/overview) integration / orchestration. | No, continue to use your current render manager | No, continue to use your current render manager |
| [6 Render Farm](#6-render-farm) | Deploys [Virtual Machine Scale Sets](https://learn.microsoft.com/azure/virtual-machine-scale-sets/overview) and/or [Kubernetes Clusters](https://learn.microsoft.com/azure/aks/intro-kubernetes) / [Fleets](https://learn.microsoft.com/azure/kubernetes-fleet/overview) for render farms. | Yes | Yes |
| [7&#160;Artist&#160;Workstation](#7-artist-workstation) | Deploys [Virtual Machines](https://learn.microsoft.com/azure/virtual-machines) for [Linux](https://learn.microsoft.com/azure/virtual-machines/linux/overview) and/or [Windows](https://learn.microsoft.com/azure/virtual-machines/windows/overview) remote artist workstations. | No | Yes |
| [8 GitOps](#8-gitops) | Enables [Terraform Plan](https://www.terraform.io/cli/commands/plan) and [Apply](https://www.terraform.io/cli/commands/apply) workflows via [GitHub Actions](https://docs.github.com/actions) triggered by [Pull Requests](https://docs.github.com/pull-requests). | No | No |
| [9 Render](#9-render) | Submits render farm jobs from [Linux](https://learn.microsoft.com/azure/virtual-machines/linux/overview) and/or [Windows](https://learn.microsoft.com/azure/virtual-machines/windows/overview) remote artist workstations. | No | No |