HashiCorp Terraform implementation
The HeartAI implementation of Microsoft Azure is managed with the Terraform declarative infrastructure-as-code software framework. Terraform allows for the declaration of system components using configuration files specified with the HashiCorp Configuration Language (HCL). Collections of these configuration files provide a declarative representation of HeartAI infrastructure-level components, which are synchronisable with the state of Microsoft Azure environments through the Azure Resource Manager API. Infrastructure deployment with Terraform supports HeartAI system infrastructure management in a way that is consistent, maintainable, scalable, and reproducible.
Terraform state management
To support Terraform operations, the representative state of the Terraform instance is itself stored within the Azure environment and managed by Terraform. This state is used by Terraform to map resources to its local representation, keep track of metadata, and to improve performance. Terraform uses this state to coordinate synchronisations to the Azure Resource Manager API. Prior to any operation, Terraform performs a refresh to update this state with the corresponding Azure environment. For the HeartAI Azure environment, all modifications to the Azure environment are administered through Terraform, such that the Terraform state should be the representative state of the Azure environment.
The following figure shows the process through which a user agent or client may utilise Terraform to communicate with Terraform State and Microsoft Azure to perform a sychronisation of Azure resources:
Example: Terraform configuration for Azure management
The following example shows Terraform declarations to configure Terraform to utilise the Azure Resource Manager API and coordinate with corresponding Azure resources.
This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Terraform Azure configuration | |
---|---|---|
Storage Account | Persists Terraform state | |
Key Vault access policy | Manages a SystemAssigned service principal and associates an access policy. |
|
Key Vault key | Enables Terraform state data encryption | . |
Storage Account customer managed key | Uses the above key for data encryption | |
Private Endpoint | Provides network endpoint within the corresponding Azure Private DNS zone | |
Private DNS zone A record | Resolves network endpoint within the corresponding Azure Private DNS zone |
provider "azurerm" {
features {}
}
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "rg_terraform" {
name = "sah-heartai-rg-prod-tfstate-aue-001"
location = "australiaeast"
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_storage_account" "terraform_state_storage" {
name = "tfstate29819"
resource_group_name = azurerm_resource_group.rg_terraform.name
location = azurerm_resource_group.rg_terraform.location
account_tier = "Standard"
account_replication_type = "LRS"
identity {
type = "SystemAssigned"
}
network_rules {
default_action = "Deny"
bypass = [
"AzureServices"]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_key_vault_access_policy" "keyvault_access_policy_terraform_storage" {
key_vault_id = azurerm_key_vault.keyvault.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_storage_account.terraform_state_storage.identity[0].principal_id
key_permissions = [
"Get",
"Create",
"List",
"Restore",
"Recover",
"Unwrapkey",
"Wrapkey",
"Purge",
"Encrypt",
"Decrypt",
"Sign",
"Verify"]
secret_permissions = [
"Get"]
}
resource "azurerm_key_vault_key" "terraform_storage_key" {
depends_on = [
azurerm_key_vault_access_policy.keyvault_access_policy_keyvault_client,
azurerm_key_vault_access_policy.keyvault_access_policy_terraform_storage
]
name = "sah-heartai-terraform-state-sa-key"
key_vault_id = azurerm_key_vault.keyvault.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey"]
}
resource "azurerm_storage_account_customer_managed_key" "terraform_state_storage_cmk" {
storage_account_id = azurerm_storage_account.terraform_state_storage.id
key_vault_id = azurerm_key_vault.keyvault.id
key_name = azurerm_key_vault_key.terraform_storage_key.name
}
resource "azurerm_private_endpoint" "terraform_state_storage_private_endpoint" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_subnet.snet_paas,
azurerm_storage_account.terraform_state_storage]
name = "sah-heartai-pe-sa-terraform-state-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.snet_paas.id
private_service_connection {
name = "sah-heartai-pe-sa-terraform-state-prod-aue-001"
is_manual_connection = false
private_connection_resource_id = azurerm_storage_account.terraform_state_storage.id
subresource_names = [
"blob"]
}
private_dns_zone_group {
name = azurerm_private_dns_zone.azure_storage_blob_dns_private.name
private_dns_zone_ids = [
azurerm_private_dns_zone.azure_storage_blob_dns_private.id]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
data "azurerm_private_endpoint_connection" "terraform_state_storage_private_endpoint_connection" {
depends_on = [
azurerm_resource_group.rg,
azurerm_private_endpoint.terraform_state_storage_private_endpoint]
name = azurerm_private_endpoint.terraform_state_storage_private_endpoint.name
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_private_dns_a_record" "terraform_state_storage_dns_a_record" {
depends_on = [
azurerm_resource_group.rg,
azurerm_storage_account.terraform_state_storage,
azurerm_private_dns_zone.azure_storage_blob_dns_private,
data.azurerm_private_endpoint_connection.terraform_state_storage_private_endpoint_connection]
name = lower(azurerm_storage_account.terraform_state_storage.name)
zone_name = azurerm_private_dns_zone.azure_storage_blob_dns_private.name
resource_group_name = azurerm_resource_group.rg.name
ttl = 300
records = [
data.azurerm_private_endpoint_connection.terraform_state_storage_private_endpoint_connection.private_service_connection.0.private_ip_address]
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
output terraform_state_storage_private_ip {
value = data.azurerm_private_endpoint_connection.terraform_state_storage_private_endpoint_connection.private_service_connection.0.private_ip_address
}
terraform {
backend "azurerm" {
resource_group_name = "sah-heartai-rg-prod-tfstate-aue-001"
storage_account_name = "tfstate29819"
container_name = "tfstate"
key = "terraform-tfstate-prod-dnszone"
}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.49.0"
}
postgresql = {
source = "cyrilgdn/postgresql"
}
random = {
source = "hashicorp/random"
}
}
}
Example: Azure Virtual Network
The following example shows Terraform declarations to specify an Azure Virtual Network. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Virtual Network |
---|---|
Private DNS | Provides private name resolution for Azure Storage Account network endpoints within the corresponding Azure Private DNS zone |
Virtual Network | Configures and deploys an Azure Virtual Network |
Network Watcher | Integrates an instance of Azure Network Watcher to a corresponding Azure Virtual Network |
resource "azurerm_private_dns_zone" "heartai-dns-private" {
name = "sah.heartai.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_virtual_network" "vnet" {
name = "sah-heartai-vnet-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
address_space = [
var.vnet-prod-aue-001-address-space.vnet]
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_subnet" "snet_aroworker" {
name = "sah-heartai-snet-aroworker-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [
var.vnet-prod-aue-001-address-space.snet1]
service_endpoints = [
"Microsoft.ContainerRegistry"]
enforce_private_link_endpoint_network_policies = true
}
resource "azurerm_subnet" "snet_aromaster" {
name = "sah-heartai-snet-aromaster-prod-aue-002"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [
var.vnet-prod-aue-001-address-space.snet2]
service_endpoints = [
"Microsoft.ContainerRegistry"]
enforce_private_link_service_network_policies = true
}
resource "azurerm_subnet" "snet_vpn_gateway" {
name = "GatewaySubnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [
var.vnet-prod-aue-001-address-space.snet3]
enforce_private_link_endpoint_network_policies = true
}
resource "azurerm_subnet" "snet_paas" {
name = "sah-heartai-snet-paas-prod-aue-004"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = [
var.vnet-prod-aue-001-address-space.snet4]
enforce_private_link_endpoint_network_policies = true
}
resource "azurerm_network_watcher" "network_watcher" {
name = "NetworkWatcher_australiaeast"
location = "australiaeast"
resource_group_name = "NetworkWatcherRG"
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
Example: Azure network security group
The following example shows Terraform declarations to specify the HeartAI default Azure network security group. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for network security group |
---|---|
Network security group | Specifies configuration for corresponding Azure network security group |
resource "azurerm_network_security_group" "nsg_default" {
name = "sah-heartai-nsg-default-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
security_rule {
name = "AllowVnetInBound"
description = "Allow inbound traffic from all VMs to all VMs in VNET"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "VirtualNetwork"
}
security_rule {
name = "AllowAzureLoadBalancerInBound"
description = "Allow inbound traffic from Azure Load Balancer"
priority = 101
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "AzureLoadBalancer"
destination_address_prefix = "*"
}
security_rule {
name = "DenyAllInBound"
description = "Deny all inbound traffic"
priority = 102
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "AllowVnetOutBound"
description = "Allow outbound traffic from all VMs to all VMs in VNET"
priority = 100
direction = "Outbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "VirtualNetwork"
destination_address_prefix = "VirtualNetwork"
}
security_rule {
name = "AllowInternetOutBound"
description = "Allow outbound traffic from all VMs to Internet"
priority = 101
direction = "Outbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "Internet"
}
security_rule {
name = "DenyAllOutBound"
description = "Deny all outbound traffic"
priority = 102
direction = "Outbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
Example: Azure Storage Account virtual network linking
The following example shows Terraform declarations to expose Azure Storage Account network endpoints to the corresponding Azure Virtual Network. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Storage Account virtual network linking |
---|---|
Private DNS | Provides private name resolution for Azure Storage Account network endpoints within the corresponding Private DNS zone |
Virtual Network Link | Exposes Azure Storage Account network endpoints within the corresponding Azure Virtual Network |
resource "azurerm_private_dns_zone" "azure_storage_blob_dns_private" {
depends_on = [
azurerm_resource_group.rg]
name = "privatelink.blob.core.windows.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "azure_storage_blob_dns_vnet_link" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_private_dns_zone.azure_storage_blob_dns_private]
name = "sah-heartai-sa-blob-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.azure_storage_blob_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone" "azure_storage_table_dns_private" {
depends_on = [
azurerm_resource_group.rg]
name = "privatelink.table.core.windows.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "azure_storage_table_dns_vnet_link" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_private_dns_zone.azure_storage_table_dns_private]
name = "sah-heartai-sa-table-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.azure_storage_table_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone" "azure_storage_queue_dns_private" {
depends_on = [
azurerm_resource_group.rg]
name = "privatelink.queue.core.windows.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "azure_storage_queue_dns_vnet_link" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_private_dns_zone.azure_storage_queue_dns_private]
name = "sah-heartai-sa-queue-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.azure_storage_queue_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
}
resource "azurerm_private_dns_zone" "azure_storage_file_dns_private" {
depends_on = [
azurerm_resource_group.rg]
name = "privatelink.file.core.windows.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "azure_storage_file_dns_vnet_link" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_private_dns_zone.azure_storage_file_dns_private]
name = "sah-heartai-sa-file-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.azure_storage_file_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
Example: Azure Key Vault
The following example shows Terraform declarations to configure and deploy an instance of Azure Key Vault. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Key Vault |
---|---|
Azure Key Vault | Configure and deploy an instance of Azure Key Vault |
resource "azurerm_key_vault" "keyvault" {
name = "sah-heartai-kv-prod"
resource_group_name = azurerm_resource_group.rg_keyvault.name
location = azurerm_resource_group.rg_keyvault.location
enabled_for_disk_encryption = true
tenant_id = data.azurerm_client_config.client.tenant_id
soft_delete_retention_days = var.kv_prod_aue_001_soft_delete_retention_days
purge_protection_enabled = true
sku_name = "standard"
network_acls {
default_action = "Deny"
bypass = "AzureServices"
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
Example: Azure Key Vault private endpoint
The following Terraform declaration shows the HeartAI production environment Azure Private Link private endpoint for the system instance of Azure Key Vault. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Key Vault private endpoint |
---|---|
Private DNS | Provides private name resolution for Azure Key Vault network endpoints within the corresponding Private DNS zone |
Storage Account customer managed key | Uses the above key for data encryption |
Private endpoint | Provides network endpoint within the corresponding Azure Private DNS zone |
Private DNS zone A record | Resolves network endpoint within the corresponding Azure Private DNS zone |
Virtual Network Link | Exposes Azure Key Vault network endpoints within the corresponding Azure Virtual Network |
resource "azurerm_private_dns_zone" "keyvault_dns_private" {
name = "privatelink.vaultcore.azure.net"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_endpoint" "keyvault_private_endpoint" {
depends_on = [
azurerm_key_vault.keyvault]
name = "sah-heartai-pe-kv-prod-aue-001"
location = azurerm_resource_group.rg_keyvault.location
resource_group_name = azurerm_resource_group.rg_keyvault.name
subnet_id = azurerm_subnet.snet_paas.id
private_service_connection {
name = "sah-heartai-pe-kv-prod-aue-001"
is_manual_connection = false
private_connection_resource_id = azurerm_key_vault.keyvault.id
subresource_names = [
"vault"]
}
private_dns_zone_group {
name = azurerm_private_dns_zone.keyvault_dns_private.name
private_dns_zone_ids = [azurerm_private_dns_zone.keyvault_dns_private.id]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
data "azurerm_private_endpoint_connection" "keyvault_private_endpoint_connection" {
depends_on = [
azurerm_private_endpoint.keyvault_private_endpoint]
name = azurerm_private_endpoint.keyvault_private_endpoint.name
resource_group_name = azurerm_resource_group.rg_keyvault.name
}
resource "azurerm_private_dns_a_record" "keyvault_dns_a_record" {
depends_on = [
azurerm_key_vault.keyvault]
name = lower(azurerm_key_vault.keyvault.name)
zone_name = azurerm_private_dns_zone.keyvault_dns_private.name
resource_group_name = azurerm_resource_group.rg.name
ttl = 300
records = [
data.azurerm_private_endpoint_connection.keyvault_private_endpoint_connection.private_service_connection.0.private_ip_address]
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "keyvault_dns_vnet_link" {
name = "sah-heartai-keyvault-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.keyvault_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
output keyvault_private_ip {
value = data.azurerm_private_endpoint_connection.keyvault_private_endpoint_connection.private_service_connection.0.private_ip_address
}
Example: Azure Key Vault logging and monitoring
The following example shows Terraform declarations to manage resources for logging and monitoring of the HeartAI instance of Azure Key Vault. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Key Vault logging and monitoring | |
---|---|---|
Storage Account | Configures and deploys an Azure Storage Account assigned to a SystemAssigned service principal |
|
Log Analytics | Ingests data and provides observability | |
Monitor | Ingests data and provides observability | |
Key Vault access policy | Assigns an access policy to the SystemAssigned service principal |
|
Key Vault key | Enables data encryption | . |
Storage Account customer managed key | Uses the above key for data encryption | |
Private Endpoint | Provides network endpoint within Azure Private DNS zone | |
Private DNS zone A record | Resolves network endpoint within Azure Private DNS zone |
resource "azurerm_storage_account" "keyvault_logs_storage" {
name = "sahheartaisakvaue001"
resource_group_name = azurerm_resource_group.rg_keyvault.name
location = azurerm_resource_group.rg_keyvault.location
account_tier = "Standard"
account_replication_type = "LRS"
identity {
type = "SystemAssigned"
}
network_rules {
default_action = "Deny"
bypass = [
"AzureServices"]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_log_analytics_workspace" "keyvault_log_analytics" {
name = "sah-heartai-la-kv-aue-001"
resource_group_name = azurerm_resource_group.rg_keyvault.name
location = azurerm_resource_group.rg_keyvault.location
sku = "PerGB2018"
retention_in_days = var.la-kv-aue-001-retention-in-days
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_monitor_diagnostic_setting" "keyvault_monitoring" {
name = "sah-heartai-mnt-kv-aue-001"
target_resource_id = azurerm_key_vault.keyvault.id
storage_account_id = azurerm_storage_account.keyvault_logs_storage.id
log_analytics_workspace_id = azurerm_log_analytics_workspace.keyvault_log_analytics.id
log {
category = "AuditEvent"
enabled = true
retention_policy {
days = 0
enabled = false
}
}
metric {
category = "AllMetrics"
enabled = true
retention_policy {
days = 0
enabled = false
}
}
}
resource "azurerm_key_vault_access_policy" "keyvault_access_policy_keyvault_logs_storage" {
key_vault_id = azurerm_key_vault.keyvault.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_storage_account.keyvault_logs_storage.identity[0].principal_id
key_permissions = [
"Get",
"Create",
"List",
"Restore",
"Recover",
"Unwrapkey",
"Wrapkey",
"Purge",
"Encrypt",
"Decrypt",
"Sign",
"Verify"]
secret_permissions = [
"Get"]
}
resource "azurerm_key_vault_key" "keyvault_logs_storage_key" {
depends_on = [
azurerm_key_vault_access_policy.keyvault_access_policy_keyvault_client,
azurerm_key_vault_access_policy.keyvault_access_policy_keyvault_logs_storage
]
name = "sah-heartai-kv-storage-key"
key_vault_id = azurerm_key_vault.keyvault.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey"]
}
resource "azurerm_storage_account_customer_managed_key" "keyvault_logs_storage_cmk" {
storage_account_id = azurerm_storage_account.keyvault_logs_storage.id
key_vault_id = azurerm_key_vault.keyvault.id
key_name = azurerm_key_vault_key.keyvault_logs_storage_key.name
}
resource "azurerm_private_endpoint" "keyvault_logs_storage_private_endpoint" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_subnet.snet_paas,
azurerm_storage_account.keyvault_logs_storage]
name = "sah-heartai-pe-sa-kv-logs-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.snet_paas.id
private_service_connection {
name = "sah-heartai-pe-sa-kv-logs-prod-aue-001"
is_manual_connection = false
private_connection_resource_id = azurerm_storage_account.keyvault_logs_storage.id
subresource_names = [
"blob"]
}
private_dns_zone_group {
name = azurerm_private_dns_zone.azure_storage_blob_dns_private.name
private_dns_zone_ids = [
azurerm_private_dns_zone.azure_storage_blob_dns_private.id]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
data "azurerm_private_endpoint_connection" "keyvault_logs_storage_private_endpoint_connection" {
depends_on = [
azurerm_resource_group.rg,
azurerm_private_endpoint.keyvault_logs_storage_private_endpoint]
name = azurerm_private_endpoint.keyvault_logs_storage_private_endpoint.name
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_private_dns_a_record" "keyvault_logs_storage_dns_a_record" {
depends_on = [
azurerm_resource_group.rg,
azurerm_storage_account.keyvault_logs_storage,
azurerm_private_dns_zone.azure_storage_blob_dns_private,
data.azurerm_private_endpoint_connection.keyvault_logs_storage_private_endpoint_connection]
name = lower(azurerm_storage_account.keyvault_logs_storage.name)
zone_name = azurerm_private_dns_zone.azure_storage_blob_dns_private.name
resource_group_name = azurerm_resource_group.rg.name
ttl = 300
records = [
data.azurerm_private_endpoint_connection.keyvault_logs_storage_private_endpoint_connection.private_service_connection.0.private_ip_address]
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
output keyvault_logs_storage_private_ip {
value = data.azurerm_private_endpoint_connection.keyvault_logs_storage_private_endpoint_connection.private_service_connection.0.private_ip_address
}
Example: Azure VPN Gateway
The following example shows Terraform declarations to deploy an instance of Azure VPN Gateway. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure VPN Gateway |
---|---|
Key Vault certificate | Root certificate for corresponding Azure VPN Gateway client |
Public IP | Allocates and associates a public IP address |
VPN Gateway | Configures and deploys Azure VPN Gateway |
resource "azurerm_key_vault_certificate" "vgw_root_certificate" {
name = "sah-heartai-vgw-root-cert-003"
key_vault_id = azurerm_key_vault.keyvault.id
certificate_policy {
issuer_parameters {
name = "Self"
}
key_properties {
exportable = true
key_size = 2048
key_type = "RSA"
reuse_key = true
}
lifetime_action {
action {
action_type = "AutoRenew"
}
trigger {
days_before_expiry = 30
}
}
secret_properties {
content_type = "application/x-pkcs12"
}
x509_certificate_properties {
extended_key_usage = [
"1.3.6.1.5.5.7.3.1",
"1.3.6.1.5.5.7.3.2"]
key_usage = [
"cRLSign",
"dataEncipherment",
"digitalSignature",
"keyAgreement",
"keyCertSign",
"keyEncipherment",
]
subject_alternative_names {
dns_names = [
"sah.heartai.net"]
}
subject = "CN=heartai-vpn-gateway"
validity_in_months = 12
}
}
}
resource "azurerm_public_ip" "vpn_gateway_ip" {
name = "sah-heartai-pip-vgw-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
allocation_method = "Dynamic"
}
resource "azurerm_virtual_network_gateway" "vpn_gateway" {
name = "sah-heartai-vgw-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
type = "Vpn"
vpn_type = "RouteBased"
active_active = false
enable_bgp = false
sku = "VpnGw1"
ip_configuration {
name = "sah-heartai-vgw-ipconfig-prod-aue-001"
public_ip_address_id = azurerm_public_ip.vpn_gateway_ip.id
private_ip_address_allocation = "Dynamic"
subnet_id = azurerm_subnet.snet_vpn_gateway.id
}
vpn_client_configuration {
address_space = [
var.heartai-prod-vgw-address-space]
root_certificate {
name = "VPNROOT"
public_cert_data = azurerm_key_vault_certificate.vgw_root_certificate.certificate_data_base64
}
}
}
Example: Azure Database for PostgreSQL
The following example shows Terraform declarations to configure and deploy an instance of Azure Database for PostgreSQL. This implementation coordinates instances of the following Azure components:
Azure component | Functionality for Azure Database for PostgreSQL | |
---|---|---|
Private DNS | Provides private name resolution for Azure Database for PostgreSQL network endpoints within the corresponding Private DNS zone | |
Azure Database for PostgreSQL | Configures and deploys an instance of Azure Database for PostgreSQL | |
Virtual Network Link | Exposes Azure Database for PostgreSQL network endpoints within the corresponding Azure Virtual Network | |
Private endpoint | Provides network endpoint within the corresponding Azure Private DNS zone | |
Private DNS zone A record | Resolves Azure Database for PostgreSQL network endpoints within the corresponding Azure Private DNS zone | |
Key Vault access policy | Assigns an access policy to the Azure Database for PostgresSQL SystemAssigned service principal. |
|
Key Vault key | Enables data encryption | . |
PostgreSQL server key | Uses the above key for data encryption |
resource "azurerm_private_dns_zone" "postgresql_dns_private" {
depends_on = [
azurerm_resource_group.rg]
name = "privatelink.postgres.database.azure.com"
resource_group_name = azurerm_resource_group.rg.name
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_postgresql_server" "postgresql" {
name = "sah-heartai-psql-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
administrator_login = var.postgres_admin_credentials_id
administrator_login_password = var.postgres_admin_credentials_key
sku_name = "GP_Gen5_2"
version = "11"
storage_mb = 51200
backup_retention_days = var.postgres_backup_retention_days
geo_redundant_backup_enabled = true
auto_grow_enabled = true
public_network_access_enabled = false
ssl_enforcement_enabled = true
ssl_minimal_tls_version_enforced = "TLS1_2"
identity {
type = "SystemAssigned"
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_private_dns_zone_virtual_network_link" "postgresql_dns_vnet_link" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_private_dns_zone.postgresql_dns_private]
name = "sah-heartai-psql-vnetlink-prod-aue-001"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.postgresql_dns_private.name
virtual_network_id = azurerm_virtual_network.vnet.id
}
resource "azurerm_private_endpoint" "postgresql_private_endpoint" {
depends_on = [
azurerm_resource_group.rg,
azurerm_virtual_network.vnet,
azurerm_subnet.snet_paas,
azurerm_postgresql_server.postgresql]
name = "sah-heartai-pe-psql-prod-aue-001"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
subnet_id = azurerm_subnet.snet_paas.id
private_service_connection {
name = "sah-heartai-pe-psql-prod-aue-001"
is_manual_connection = false
private_connection_resource_id = azurerm_postgresql_server.postgresql.id
subresource_names = [
"postgresqlServer"]
}
private_dns_zone_group {
name = azurerm_private_dns_zone.postgresql_dns_private.name
private_dns_zone_ids = [
azurerm_private_dns_zone.postgresql_dns_private.id]
}
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
data "azurerm_private_endpoint_connection" "postgres_private_endpoint_connection" {
depends_on = [
azurerm_resource_group.rg,
azurerm_private_endpoint.postgresql_private_endpoint]
name = azurerm_private_endpoint.postgresql_private_endpoint.name
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_private_dns_a_record" "postgresql_dns_a_record" {
depends_on = [
azurerm_resource_group.rg,
azurerm_postgresql_server.postgresql,
azurerm_private_dns_zone.postgresql_dns_private,
data.azurerm_private_endpoint_connection.postgres_private_endpoint_connection]
name = lower(azurerm_postgresql_server.postgresql.name)
zone_name = azurerm_private_dns_zone.postgresql_dns_private.name
resource_group_name = azurerm_resource_group.rg.name
ttl = 300
records = [
data.azurerm_private_endpoint_connection.postgres_private_endpoint_connection.private_service_connection.0.private_ip_address]
tags = {
"Application Name" = var.heartai-environment.application-name
"Application Owner" = var.heartai-environment.application-owner
"Environment" = var.heartai-environment.environment
"Division Department" = var.heartai-environment.division-department
"Cost Centre" = var.heartai-environment.cost-centre
}
}
resource "azurerm_key_vault_access_policy" "keyvault_access_policy_postgresql_server" {
depends_on = [
azurerm_key_vault.keyvault,
data.azurerm_client_config.client,
azurerm_postgresql_server.postgresql]
key_vault_id = azurerm_key_vault.keyvault.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_postgresql_server.postgresql.identity[0].principal_id
key_permissions = [
"Get",
"Unwrapkey",
"Wrapkey"]
secret_permissions = [
"Get"]
}
resource "azurerm_key_vault_key" "postgresql_key" {
depends_on = [
azurerm_key_vault_access_policy.keyvault_access_policy_keyvault_client,
azurerm_key_vault_access_policy.keyvault_access_policy_postgresql_server]
name = "sah-heartai-psql-key"
key_vault_id = azurerm_key_vault.keyvault.id
key_type = "RSA"
key_size = 2048
key_opts = [
"decrypt",
"encrypt",
"sign",
"unwrapKey",
"verify",
"wrapKey"]
}
resource "azurerm_postgresql_server_key" "postgresql_key" {
depends_on = [
azurerm_postgresql_server.postgresql,
azurerm_key_vault_key.postgresql_key]
server_id = azurerm_postgresql_server.postgresql.id
key_vault_key_id = azurerm_key_vault_key.postgresql_key.id
}
Example: PostgreSQL database and users
The following example shows Terraform declarations to provision and reference a database and users of a corresponding PostgreSQL instance. This implementation coordinates instances of the following PostgreSQL components:
PostgreSQL component | Functionality for PostgreSQL |
---|---|
Provider | References an instance of PostgreSQL |
Role | Creates PostgreSQL identity principal and assigns secure password |
Database | Creates PostgreSQL database |
Default privileges | Associates permissions to PostgreSQL database |
//provider "postgresql" {
// host = azurerm_private_dns_a_record.postgresql_dns_a_record.fqdn
// port = 5432
// username = var.postgres_admin_credentials_id
// password = var.postgres_admin_credentials_key
// sslmode = "require"
// connect_timeout = 30
// superuser = false
//}
//
//variable "hello_world_prod_database_name" {
// type = string
// default = "hello_world_prod"
//}
//
//resource "random_id" "hello_world_prod_password_admin" {
// byte_length = var.hello_world_prod_password_byte_length
//}
//
//resource "random_id" "hello_world_prod_password_user" {
// byte_length = var.hello_world_prod_password_byte_length
//}
//
//resource "postgresql_role" "hello_world_prod_admin" {
// name = "${var.hello_world_prod_database_name}_admin"
// password = random_id.hello_world_prod_password_admin.hex
// login = true
// create_database = "false"
//}
//
//resource "postgresql_role" "hello_world_prod_user" {
// name = "${var.hello_world_prod_database_name}_user"
// password = random_id.hello_world_prod_password_user.hex
// login = true
// create_database = "false"
//}
//
//resource "postgresql_database" "hello_world_prod_database" {
// name = var.hello_world_prod_database_name
// owner = var.postgres_admin_credentials_id
// lc_collate = "en_US.UTF-8"
// lc_ctype = "en_US.UTF-8"
// encoding = "UTF8"
//}
//
//resource "postgresql_default_privileges" "hello_world_prod_table" {
// depends_on = [
// postgresql_database.hello_world_prod_database,
// postgresql_role.hello_world_prod_admin,
// postgresql_role.hello_world_prod_user]
// database = var.hello_world_prod_database_name
// owner = "${var.hello_world_prod_database_name}_admin"
// role = "${var.hello_world_prod_database_name}_user"
// schema = "public"
// object_type = "table"
// privileges = [
// "ALL"]
//}
//
//output "hello_world_prod_database_admin_key" {
// sensitive = true
// value = random_id.hello_world_prod_password_admin.hex
//}
//
//output "hello_world_prod_database_user_key" {
// sensitive = true
// value = random_id.hello_world_prod_password_user.hex
//}