diff --git a/quickstart/101-azure-virtual-desktop-azapi/afstorage.tf b/quickstart/101-azure-virtual-desktop-azapi/afstorage.tf new file mode 100644 index 000000000..7f3e3146b --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/afstorage.tf @@ -0,0 +1,58 @@ +# Create a Resource Group for Storage +resource "azurerm_resource_group" "rg_storage" { + location = var.deploy_location + name = var.rg_stor +} + +# Generate a random string (consisting of four characters) +resource "random_string" "random" { + length = 4 + upper = false + special = false +} + +# Create a File Storage Account using AzAPI +resource "azapi_resource" "storage" { + type = "Microsoft.Storage/storageAccounts@2023-05-01" + name = "stor${random_string.random.id}" + location = azurerm_resource_group.rg_storage.location + parent_id = azurerm_resource_group.rg_storage.id + + body = { + kind = "FileStorage" + sku = { + name = "Premium_LRS" + } + properties = {} + } + + response_export_values = ["properties", "id"] + + # Storage API returns many defaulted properties + lifecycle { + ignore_changes = [body] + } +} + +# Create File Share using AzAPI +resource "azapi_resource" "fs_share" { + type = "Microsoft.Storage/storageAccounts/fileServices/shares@2023-05-01" + name = "fslogix" + parent_id = "${azapi_resource.storage.id}/fileServices/default" + + body = { + properties = {} + } +} + +# Azure built-in roles - keep as azurerm +data "azurerm_role_definition" "storage_role" { + name = "Storage File Data SMB Share Contributor" +} + +resource "azurerm_role_assignment" "af_role" { + count = var.enable_ad_integration ? 1 : 0 + scope = azapi_resource.storage.id + role_definition_id = data.azurerm_role_definition.storage_role.id + principal_id = azuread_group.aad_group[0].id +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/host.tf b/quickstart/101-azure-virtual-desktop-azapi/host.tf new file mode 100644 index 000000000..b48e620f7 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/host.tf @@ -0,0 +1,190 @@ +locals { + registration_token = azurerm_virtual_desktop_host_pool_registration_info.registrationinfo.token +} + +resource "random_string" "AVD_local_password" { + count = var.rdsh_count + length = 16 + special = true + min_special = 2 + override_special = "*!@#?" +} + +resource "azurerm_resource_group" "rg" { + name = var.rg + location = var.resource_group_location +} + +# Create Network Interfaces using AzAPI +resource "azapi_resource" "avd_vm_nic" { + count = var.rdsh_count + type = "Microsoft.Network/networkInterfaces@2024-01-01" + name = "${var.prefix}-${count.index + 1}-nic" + location = azurerm_resource_group.rg.location + parent_id = azurerm_resource_group.rg.id + + body = { + properties = { + ipConfigurations = [ + { + name = "nic${count.index + 1}_config" + properties = { + privateIPAllocationMethod = "Dynamic" + subnet = { + id = azapi_resource.vnet.output.properties.subnets[0].id + } + } + } + ] + } + } + + # Retry on transient delete errors (Azure holds NIC for 180s after VM deletion) + retry = { + error_message_regex = ["NicReservedForAnotherVm", "InUseSubnetCannotBeDeleted", "OperationNotAllowed"] + interval_seconds = 30 + max_interval_seconds = 180 + } + + # Azure returns allocated IP and additional defaulted fields + lifecycle { + ignore_changes = [body] + } + + # NICs must be destroyed before NSG association is removed + depends_on = [azapi_update_resource.nsg_assoc] +} + +# Create Windows Virtual Machines using AzAPI +resource "azapi_resource" "avd_vm" { + count = var.rdsh_count + type = "Microsoft.Compute/virtualMachines@2024-03-01" + name = "${var.prefix}-${count.index + 1}" + location = azurerm_resource_group.rg.location + parent_id = azurerm_resource_group.rg.id + + body = { + properties = { + hardwareProfile = { + vmSize = var.vm_size + } + osProfile = { + computerName = "${var.prefix}-${count.index + 1}" + adminUsername = var.local_admin_username + adminPassword = var.local_admin_password + windowsConfiguration = { + provisionVMAgent = true + } + } + storageProfile = { + osDisk = { + name = "${lower(var.prefix)}-${count.index + 1}" + caching = "ReadWrite" + createOption = "FromImage" + managedDisk = { + storageAccountType = "Standard_LRS" + } + } + imageReference = { + publisher = "MicrosoftWindowsDesktop" + offer = "Windows-10" + sku = "win10-22h2-avd" + version = "latest" + } + } + networkProfile = { + networkInterfaces = [ + { + id = azapi_resource.avd_vm_nic[count.index].id + } + ] + } + } + } + + depends_on = [azapi_resource.avd_vm_nic] + + retry = { + error_message_regex = ["SkuNotAvailable", "OperationNotAllowed", "AllocationFailed"] + interval_seconds = 60 + max_interval_seconds = 300 + } + + # Write-only adminPassword and Azure-defaulted VM properties + lifecycle { + ignore_changes = [body] + } +} + +# Domain Join Extension using AzAPI +resource "azapi_resource" "domain_join" { + count = var.enable_ad_integration ? var.rdsh_count : 0 + type = "Microsoft.Compute/virtualMachines/extensions@2024-03-01" + name = "${var.prefix}-${count.index + 1}-domainJoin" + location = azurerm_resource_group.rg.location + parent_id = azapi_resource.avd_vm[count.index].id + + body = { + properties = { + publisher = "Microsoft.Compute" + type = "JsonADDomainExtension" + typeHandlerVersion = "1.3" + autoUpgradeMinorVersion = true + settings = { + Name = var.domain_name + OUPath = var.ou_path + User = "${var.domain_user_upn}@${var.domain_name}" + Restart = "true" + Options = "3" + } + protectedSettings = { + Password = var.domain_password + } + } + } + + # Write-only protectedSettings not returned by GET + lifecycle { + ignore_changes = [body] + } +} + +# DSC Extension for AVD agent using AzAPI +resource "azapi_resource" "vmext_dsc" { + count = var.rdsh_count + type = "Microsoft.Compute/virtualMachines/extensions@2024-03-01" + name = "${var.prefix}${count.index + 1}-avd_dsc" + location = azurerm_resource_group.rg.location + parent_id = azapi_resource.avd_vm[count.index].id + + body = { + properties = { + publisher = "Microsoft.Powershell" + type = "DSC" + typeHandlerVersion = "2.73" + autoUpgradeMinorVersion = true + settings = { + modulesUrl = "https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_1.0.02714.342.zip" + configurationFunction = "Configuration.ps1\\AddSessionHost" + properties = { + HostPoolName = azurerm_virtual_desktop_host_pool.hostpool.name + } + } + protectedSettings = { + properties = { + registrationInfoToken = local.registration_token + } + } + } + } + + # Write-only protectedSettings not returned by GET + lifecycle { + ignore_changes = [body] + } + + depends_on = [ + azapi_resource.avd_vm, + azurerm_virtual_desktop_host_pool.hostpool + ] +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/loganalytics.tf b/quickstart/101-azure-virtual-desktop-azapi/loganalytics.tf new file mode 100644 index 000000000..fed75ba35 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/loganalytics.tf @@ -0,0 +1,21 @@ +resource "azurerm_resource_group" "shared" { + name = var.rg_shared_name + location = var.deploy_location +} + +# Creates Log Analytics Workspace using AzAPI +resource "azapi_resource" "law" { + type = "Microsoft.OperationalInsights/workspaces@2023-09-01" + name = "log${random_string.random.id}" + location = azurerm_resource_group.shared.location + parent_id = azurerm_resource_group.shared.id + + body = { + properties = { + sku = { + name = "PerGB2018" + } + retentionInDays = 30 + } + } +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/main.tf b/quickstart/101-azure-virtual-desktop-azapi/main.tf new file mode 100644 index 000000000..7a77067c2 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/main.tf @@ -0,0 +1,77 @@ +# Resource group for AVD service objects +resource "azurerm_resource_group" "sh" { + name = var.rg_name + location = var.resource_group_location +} + +# Create AVD workspace using AzAPI +resource "azapi_resource" "workspace" { + type = "Microsoft.DesktopVirtualization/workspaces@2024-04-03" + name = var.workspace + location = azurerm_resource_group.sh.location + parent_id = azurerm_resource_group.sh.id + + body = { + properties = { + friendlyName = "${var.prefix} Workspace" + description = "${var.prefix} Workspace" + } + } +} + +# Create AVD host pool - keep as azurerm (registration token not easily available via azapi) +resource "azurerm_virtual_desktop_host_pool" "hostpool" { + resource_group_name = azurerm_resource_group.sh.name + location = azurerm_resource_group.sh.location + name = var.hostpool + friendly_name = var.hostpool + validate_environment = true + custom_rdp_properties = "audiocapturemode:i:1;audiomode:i:0;" + description = "${var.prefix} Terraform HostPool" + type = "Pooled" + maximum_sessions_allowed = 16 + load_balancer_type = "DepthFirst" +} + +resource "azurerm_virtual_desktop_host_pool_registration_info" "registrationinfo" { + hostpool_id = azurerm_virtual_desktop_host_pool.hostpool.id + expiration_date = coalesce(var.rfc3339, timeadd(timestamp(), "23h")) +} + +# Create AVD Application Group using AzAPI +resource "azapi_resource" "dag" { + type = "Microsoft.DesktopVirtualization/applicationGroups@2024-04-03" + name = "${var.prefix}-dag" + location = azurerm_resource_group.sh.location + parent_id = azurerm_resource_group.sh.id + + body = { + properties = { + friendlyName = "Desktop AppGroup" + description = "AVD application group" + hostPoolArmPath = azurerm_virtual_desktop_host_pool.hostpool.id + applicationGroupType = "Desktop" + } + } + + depends_on = [azurerm_virtual_desktop_host_pool.hostpool, azapi_resource.workspace] +} + +# Associate Workspace and DAG - update workspace with application group reference +resource "azapi_update_resource" "ws_dag_association" { + type = "Microsoft.DesktopVirtualization/workspaces@2024-04-03" + name = var.workspace + parent_id = azurerm_resource_group.sh.id + + body = { + properties = { + applicationGroupReferences = [azapi_resource.dag.id] + } + } + + lifecycle { + ignore_changes = [body] + } + + depends_on = [azapi_resource.workspace, azapi_resource.dag] +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/networking.tf b/quickstart/101-azure-virtual-desktop-azapi/networking.tf new file mode 100644 index 000000000..22a036257 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/networking.tf @@ -0,0 +1,130 @@ +# Create Virtual Network using AzAPI +resource "azapi_resource" "vnet" { + type = "Microsoft.Network/virtualNetworks@2024-01-01" + name = "${var.prefix}-VNet" + location = var.deploy_location + parent_id = azurerm_resource_group.rg.id + + body = { + properties = { + addressSpace = { + addressPrefixes = var.vnet_range + } + dhcpOptions = { + dnsServers = var.dns_servers + } + subnets = [ + { + name = "default" + properties = { + addressPrefix = var.subnet_range[0] + } + } + ] + } + } + + response_export_values = ["properties.subnets"] + + # Subnet mutation by nsg_assoc causes drift on the parent VNet + lifecycle { + ignore_changes = [body] + } +} + +# Create Network Security Group using AzAPI +resource "azapi_resource" "nsg" { + type = "Microsoft.Network/networkSecurityGroups@2024-01-01" + name = "${var.prefix}-NSG" + location = var.deploy_location + parent_id = azurerm_resource_group.rg.id + + body = { + properties = { + securityRules = [ + { + name = "HTTPS" + properties = { + priority = 1001 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + sourcePortRange = "*" + destinationPortRange = "443" + sourceAddressPrefix = "*" + destinationAddressPrefix = "*" + } + } + ] + } + } + + # Retry on transient delete errors (NSG still in use while NICs are being released) + retry = { + error_message_regex = ["InUseNetworkSecurityGroupCannotBeDeleted", "InUseSubnetCannotBeDeleted"] + interval_seconds = 30 + max_interval_seconds = 180 + } +} + +# Associate NSG with subnet +resource "azapi_update_resource" "nsg_assoc" { + type = "Microsoft.Network/virtualNetworks/subnets@2024-01-01" + name = "default" + parent_id = azapi_resource.vnet.id + + body = { + properties = { + addressPrefix = var.subnet_range[0] + networkSecurityGroup = { + id = azapi_resource.nsg.id + } + } + } + + # API response includes normalized properties causing plan drift + lifecycle { + ignore_changes = [body] + } + + depends_on = [azapi_resource.vnet, azapi_resource.nsg] +} + +# Data source to get existing AD VNet (only when AD integration is enabled) +data "azurerm_virtual_network" "ad_vnet_data" { + count = var.enable_ad_integration ? 1 : 0 + name = var.ad_vnet + resource_group_name = var.ad_rg +} + +# VNet peering: AVD spoke -> AD +resource "azapi_resource" "peer1" { + count = var.enable_ad_integration ? 1 : 0 + type = "Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-01-01" + name = "peer_avdspoke_ad" + parent_id = azapi_resource.vnet.id + + body = { + properties = { + remoteVirtualNetwork = { + id = data.azurerm_virtual_network.ad_vnet_data[0].id + } + } + } +} + +# VNet peering: AD -> AVD spoke +resource "azapi_resource" "peer2" { + count = var.enable_ad_integration ? 1 : 0 + type = "Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-01-01" + name = "peer_ad_avdspoke" + parent_id = data.azurerm_virtual_network.ad_vnet_data[0].id + + body = { + properties = { + remoteVirtualNetwork = { + id = azapi_resource.vnet.id + } + } + } +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/outputs.tf b/quickstart/101-azure-virtual-desktop-azapi/outputs.tf new file mode 100644 index 000000000..d1e6f6001 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/outputs.tf @@ -0,0 +1,54 @@ +output "azure_virtual_desktop_compute_resource_group" { + description = "Name of the Resource group in which to deploy session host" + value = azurerm_resource_group.rg.name +} + +output "azure_virtual_desktop_host_pool" { + description = "Name of the Azure Virtual Desktop host pool" + value = azurerm_virtual_desktop_host_pool.hostpool.name +} + +output "azurerm_virtual_desktop_application_group" { + description = "Name of the Azure Virtual Desktop DAG" + value = azapi_resource.dag.name +} + +output "azurerm_virtual_desktop_workspace" { + description = "Name of the Azure Virtual Desktop workspace" + value = azapi_resource.workspace.name +} + +output "location" { + description = "The Azure region" + value = azurerm_resource_group.rg.location +} + +output "storage_account" { + description = "Storage account for Profiles" + value = azapi_resource.storage.name +} + +output "storage_account_share" { + description = "Name of the Azure File Share created for FSLogix" + value = azapi_resource.fs_share.name +} + +output "session_host_count" { + description = "The number of VMs created" + value = var.rdsh_count +} + +output "dnsservers" { + description = "Custom DNS configuration" + value = var.dns_servers +} + +output "vnetrange" { + description = "Address range for deployment vnet" + value = var.vnet_range +} + +output "AVD_user_groupname" { + description = "Azure Active Directory Group for AVD users" + value = var.enable_ad_integration ? azuread_group.aad_group[0].display_name : "" +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/providers.tf b/quickstart/101-azure-virtual-desktop-azapi/providers.tf new file mode 100644 index 000000000..05231b4b4 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/providers.tf @@ -0,0 +1,28 @@ +terraform { + required_version = ">=1.0" + + required_providers { + azapi = { + source = "Azure/azapi" + version = "~>2.0" + } + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + azuread = { + source = "hashicorp/azuread" + version = "~>2.0" + } + random = { + source = "hashicorp/random" + version = "~>3.0" + } + } +} + +provider "azapi" {} + +provider "azurerm" { + features {} +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/rbac.tf b/quickstart/101-azure-virtual-desktop-azapi/rbac.tf new file mode 100644 index 000000000..5610bf994 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/rbac.tf @@ -0,0 +1,30 @@ +# RBAC resources kept as azurerm/azuread - azapi doesn't manage RBAC well +# Only created when AD integration is enabled +data "azuread_user" "aad_user" { + for_each = var.enable_ad_integration ? toset(var.avd_users) : toset([]) + user_principal_name = format("%s", each.key) +} + +data "azurerm_role_definition" "role" { + count = var.enable_ad_integration ? 1 : 0 + name = "Desktop Virtualization User" +} + +resource "azuread_group" "aad_group" { + count = var.enable_ad_integration ? 1 : 0 + display_name = var.aad_group_name + security_enabled = true +} + +resource "azuread_group_member" "aad_group_member" { + for_each = var.enable_ad_integration ? data.azuread_user.aad_user : {} + group_object_id = azuread_group.aad_group[0].id + member_object_id = each.value["id"] +} + +resource "azurerm_role_assignment" "role" { + count = var.enable_ad_integration ? 1 : 0 + scope = azapi_resource.dag.id + role_definition_id = data.azurerm_role_definition.role[0].id + principal_id = azuread_group.aad_group[0].id +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/sig.tf b/quickstart/101-azure-virtual-desktop-azapi/sig.tf new file mode 100644 index 000000000..a38791a90 --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/sig.tf @@ -0,0 +1,45 @@ +# Creates Shared Image Gallery using AzAPI +resource "azapi_resource" "sig" { + type = "Microsoft.Compute/galleries@2022-03-03" + name = "sig${random_string.random.id}" + location = azurerm_resource_group.shared.location + parent_id = azurerm_resource_group.shared.id + + body = { + properties = { + description = "Shared images" + } + } + + tags = { + Environment = "Demo" + Tech = "Terraform" + } + + # Retry on transient delete errors (gallery may still have image definitions being removed) + retry = { + error_message_regex = ["CannotDeleteResource", "ConflictingUserInput"] + interval_seconds = 30 + max_interval_seconds = 180 + } +} + +# Creates image definition using AzAPI +resource "azapi_resource" "sig_image" { + type = "Microsoft.Compute/galleries/images@2022-03-03" + name = "avd-image" + location = azurerm_resource_group.shared.location + parent_id = azapi_resource.sig.id + + body = { + properties = { + osType = "Windows" + osState = "Generalized" + identifier = { + publisher = "MicrosoftWindowsDesktop" + offer = "office-365" + sku = "win10-22h2-avd-m365" + } + } + } +} diff --git a/quickstart/101-azure-virtual-desktop-azapi/variables.tf b/quickstart/101-azure-virtual-desktop-azapi/variables.tf new file mode 100644 index 000000000..5ce3311ab --- /dev/null +++ b/quickstart/101-azure-virtual-desktop-azapi/variables.tf @@ -0,0 +1,154 @@ +variable "resource_group_location" { + default = "eastus" + description = "Location of the resource group." +} + +variable "rg" { + type = string + default = "rg-avd-compute" + description = "Name of the Resource group in which to deploy session host" +} + +variable "rg_name" { + type = string + default = "rg-avd-resources" + description = "Name of the Resource group in which to deploy service objects" +} + +variable "rg_stor" { + type = string + default = "rg-avd-storage" + description = "Name of the Resource group in which to deploy storage" +} + +variable "rg_shared_name" { + type = string + default = "rg-shared-resources" + description = "Name of the Resource group in which to deploy shared resources" +} + +variable "deploy_location" { + type = string + default = "eastus" + description = "The Azure Region in which all resources in this example should be created." +} + +variable "workspace" { + type = string + description = "Name of the Azure Virtual Desktop workspace" + default = "AVD TF Workspace" +} + +variable "hostpool" { + type = string + description = "Name of the Azure Virtual Desktop host pool" + default = "AVD-TF-HP" +} + +variable "enable_ad_integration" { + type = bool + default = false + description = "Enable AD integration (VNet peering and RBAC). Requires pre-existing AD infrastructure." +} + +variable "ad_vnet" { + type = string + default = "infra-network" + description = "Name of domain controller vnet" +} + +variable "rfc3339" { + type = string + default = null + description = "Registration token expiration (RFC 3339). Defaults to 23 hours from apply time." +} + +variable "dns_servers" { + type = list(string) + default = ["10.0.1.4", "168.63.129.16"] + description = "Custom DNS configuration" +} + +variable "vnet_range" { + type = list(string) + default = ["10.2.0.0/16"] + description = "Address range for deployment VNet" +} + +variable "subnet_range" { + type = list(string) + default = ["10.2.0.0/24"] + description = "Address range for session host subnet" +} + +variable "ad_rg" { + type = string + default = "infra-rg" + description = "The resource group for AD VM" +} + +variable "avd_users" { + description = "AVD users" + default = [ + "avduser01@contoso.net", + "avduser02@contoso.net" + ] +} + +variable "aad_group_name" { + type = string + default = "AVDUsers" + description = "Azure Active Directory Group for AVD users" +} + +variable "rdsh_count" { + description = "Number of AVD machines to deploy" + default = 2 +} + +variable "prefix" { + type = string + default = "avdtf" + description = "Prefix of the name of the AVD machine(s)" +} + +variable "domain_name" { + type = string + default = "infra.local" + description = "Name of the domain to join" +} + +variable "domain_user_upn" { + type = string + default = "domainjoineruser" + description = "Username for domain join (do not include domain name as this is appended)" +} + +variable "domain_password" { + type = string + default = "ChangeMe123!" + description = "Password of the user to authenticate with the domain" + sensitive = true +} + +variable "vm_size" { + description = "Size of the machine to deploy" + default = "Standard_DS2_v2" +} + +variable "ou_path" { + default = "" +} + +variable "local_admin_username" { + type = string + default = "localadm" + description = "local admin username" +} + +variable "local_admin_password" { + type = string + default = "ChangeMe123!" + description = "local admin password" + sensitive = true +} diff --git a/quickstart/101-cosmos-db-azure-container-instance-azapi/aci.tf b/quickstart/101-cosmos-db-azure-container-instance-azapi/aci.tf new file mode 100644 index 000000000..11321ccea --- /dev/null +++ b/quickstart/101-cosmos-db-azure-container-instance-azapi/aci.tf @@ -0,0 +1,72 @@ +# Create Container Instance using AzAPI +resource "azapi_resource" "main" { + type = "Microsoft.ContainerInstance/containerGroups@2023-05-01" + name = "${random_pet.rg_name.id}-vote-aci" + location = azurerm_resource_group.rg.location + parent_id = azurerm_resource_group.rg.id + + body = { + properties = { + osType = "Linux" + ipAddress = { + type = "Public" + dnsNameLabel = "vote-aci-${random_integer.ri.result}" + ports = [ + { + port = 80 + protocol = "TCP" + } + ] + } + containers = [ + { + name = "vote-aci" + properties = { + image = "mcr.microsoft.com/azuredocs/aci-helloworld:latest" + resources = { + requests = { + cpu = 0.5 + memoryInGB = 1.5 + } + } + ports = [ + { + port = 80 + protocol = "TCP" + } + ] + environmentVariables = [ + { + name = "COSMOS_DB_ENDPOINT" + secureValue = azapi_resource.vote_cosmos_db.output.properties.documentEndpoint + }, + { + name = "COSMOS_DB_MASTERKEY" + secureValue = azapi_resource_action.cosmos_keys.output.primaryMasterKey + }, + { + name = "TITLE" + value = "Azure Voting App" + }, + { + name = "VOTE1VALUE" + value = "Cats" + }, + { + name = "VOTE2VALUE" + value = "Dogs" + } + ] + } + } + ] + } + } + + response_export_values = ["properties.ipAddress.fqdn"] + + # ACI API returns additional container and ipAddress properties + lifecycle { + ignore_changes = [body] + } +} diff --git a/quickstart/101-cosmos-db-azure-container-instance-azapi/main.tf b/quickstart/101-cosmos-db-azure-container-instance-azapi/main.tf new file mode 100644 index 000000000..4a80df08b --- /dev/null +++ b/quickstart/101-cosmos-db-azure-container-instance-azapi/main.tf @@ -0,0 +1,53 @@ +resource "azurerm_resource_group" "rg" { + name = "${random_pet.rg_name.id}-rg" + location = var.resource_group_location +} + +# Create Cosmos DB Account using AzAPI +resource "azapi_resource" "vote_cosmos_db" { + type = "Microsoft.DocumentDB/databaseAccounts@2024-05-15" + name = "${random_pet.rg_name.id}-${random_integer.ri.result}" + location = azurerm_resource_group.rg.location + parent_id = azurerm_resource_group.rg.id + + body = { + kind = "GlobalDocumentDB" + properties = { + databaseAccountOfferType = "Standard" + consistencyPolicy = { + defaultConsistencyLevel = "BoundedStaleness" + maxIntervalInSeconds = 10 + maxStalenessPrefix = 200 + } + locations = [ + { + locationName = azurerm_resource_group.rg.location + failoverPriority = 0 + } + ] + } + } + + response_export_values = ["properties.documentEndpoint"] + + # Cosmos DB API returns many additional properties not in the request + lifecycle { + ignore_changes = [body] + } +} + +resource "azapi_resource_action" "cosmos_keys" { + type = "Microsoft.DocumentDB/databaseAccounts@2024-05-15" + resource_id = azapi_resource.vote_cosmos_db.id + action = "listKeys" + response_export_values = ["primaryMasterKey"] +} + +resource "random_integer" "ri" { + min = 10000 + max = 99999 +} + +resource "random_pet" "rg_name" { + prefix = var.prefix +} diff --git a/quickstart/101-cosmos-db-azure-container-instance-azapi/outputs.tf b/quickstart/101-cosmos-db-azure-container-instance-azapi/outputs.tf new file mode 100644 index 000000000..c3f727a21 --- /dev/null +++ b/quickstart/101-cosmos-db-azure-container-instance-azapi/outputs.tf @@ -0,0 +1,11 @@ +output "resource_group_name" { + value = azurerm_resource_group.rg.name +} + +output "cosmosdb_account_name" { + value = azapi_resource.vote_cosmos_db.name +} + +output "dns" { + value = azapi_resource.main.output.properties.ipAddress.fqdn +} diff --git a/quickstart/101-cosmos-db-azure-container-instance-azapi/providers.tf b/quickstart/101-cosmos-db-azure-container-instance-azapi/providers.tf new file mode 100644 index 000000000..00c5b141d --- /dev/null +++ b/quickstart/101-cosmos-db-azure-container-instance-azapi/providers.tf @@ -0,0 +1,28 @@ +terraform { + required_version = ">=1.0" + + required_providers { + azapi = { + source = "Azure/azapi" + version = "~>2.0" + } + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + random = { + source = "hashicorp/random" + version = "~>3.0" + } + } +} + +provider "azapi" {} + +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } +} diff --git a/quickstart/101-cosmos-db-azure-container-instance-azapi/variables.tf b/quickstart/101-cosmos-db-azure-container-instance-azapi/variables.tf new file mode 100644 index 000000000..5172f6d2a --- /dev/null +++ b/quickstart/101-cosmos-db-azure-container-instance-azapi/variables.tf @@ -0,0 +1,10 @@ +variable "resource_group_location" { + default = "East Asia" + description = "Location of the resource group." +} + +variable "prefix" { + type = string + default = "cosmos-db-aci" + description = "Prefix of the resource name" +} diff --git a/quickstart/101-resource-group-azapi/main.tf b/quickstart/101-resource-group-azapi/main.tf new file mode 100644 index 000000000..ffaa7f8c5 --- /dev/null +++ b/quickstart/101-resource-group-azapi/main.tf @@ -0,0 +1,11 @@ +# Create a random name for the resource group using random_pet +resource "random_pet" "rg_name" { + prefix = var.resource_group_name_prefix +} + +# Create a resource group using AzAPI provider +resource "azapi_resource" "example" { + type = "Microsoft.Resources/resourceGroups@2024-03-01" + name = random_pet.rg_name.id + location = var.resource_group_location +} diff --git a/quickstart/101-resource-group-azapi/outputs.tf b/quickstart/101-resource-group-azapi/outputs.tf new file mode 100644 index 000000000..c52da06d9 --- /dev/null +++ b/quickstart/101-resource-group-azapi/outputs.tf @@ -0,0 +1,3 @@ +output "resource_group_name" { + value = azapi_resource.example.name +} diff --git a/quickstart/101-resource-group-azapi/providers.tf b/quickstart/101-resource-group-azapi/providers.tf new file mode 100644 index 000000000..d5ae9a76b --- /dev/null +++ b/quickstart/101-resource-group-azapi/providers.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + azapi = { + source = "Azure/azapi" + version = "~>2.0" + } + random = { + source = "hashicorp/random" + version = "~>3.0" + } + } +} + +provider "azapi" {} diff --git a/quickstart/101-resource-group-azapi/variables.tf b/quickstart/101-resource-group-azapi/variables.tf new file mode 100644 index 000000000..fb7b479c6 --- /dev/null +++ b/quickstart/101-resource-group-azapi/variables.tf @@ -0,0 +1,11 @@ +variable "resource_group_location" { + type = string + default = "eastus" + description = "Location of the resource group." +} + +variable "resource_group_name_prefix" { + type = string + default = "rg" + description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription." +}