Follow this guide to provision Supermetal BYOC (Bring Your Own Cloud), which deploys Supermetal's data replication agents in your own Azure virtual cloud environment, maintaining full control and ownership over your data.
This provides an additional layer of security and isolation. Supermetal handles provisioning, operations, and maintenance.
Supermetal's BYOC model for Azure separates responsibilities between Supermetal's control plane and your Azure environment. The data replication agents run within your Azure Virtual Network (VNet), giving you full control over your data while Supermetal manages the agent lifecycle.
Azure Subscription ID (subscription_id): The ID of your Azure subscription where resources will be deployed
Azure Region (location): The Azure region for deployment (e.g., eastus2, westeurope)
Resource Group (resource_group_name): The name of a Resource Group where Supermetal will deploy resources
VNet ID (vnet_id): The resource ID of your existing Virtual Network where Supermetal agents will be deployed
Subnet IDs (private_subnet_ids): A list of subnet resource IDs within your specified VNet. Supermetal agents will be deployed in these subnets. Ensure these subnets are in the same region as your source (and target) databases
(Optional) Key Vault ID (credentials_key_vault_id): Decide if you'll use an existing Azure Key Vault for storing sensitive connector credentials. If you have an existing Key Vault, have its resource ID ready. If left blank, the bootstrap process will create a new Key Vault specifically for Supermetal
The bootstrap process uses a Terraform script provided by Supermetal to provision the necessary resources, including the Service Principal, Role, and private endpoint.
These resources enable Supermetal to automatically register your Azure environment with the Supermetal control plane over the private endpoint, and to deploy Supermetal data plane / agents in your VNet.
Environment Bootstrap
The initial environment bootstrap process does not provision any compute resources or the data plane / agents. Once the environment bootstrap is complete, you can create connectors to deploy Supermetal data plane / agents on-demand in your VNet.
Set up your Azure credentials for Terraform. This typically involves authenticating with the Azure CLI using az login and ensuring you have the appropriate permissions.
Verify access by running az account show. You should see your Azure subscription details.
Create a new directory for your Terraform configuration.
If using the appendix script, save the Terraform code into files within this directory (e.g., main.tf).
Create a terraform.tfvars file in the same directory to specify the required variable values. Alternatively, you can pass variables via the command line.
Monitor the terraform apply command output. It will indicate when the resources have been successfully created.
Once the Terraform apply is complete, Supermetal's Control Plane automatically performs a registration process for the new environment.
Monitor the Environments page in your Supermetal Console. The environment should show up with a "Ready" status within a few minutes after Terraform completes.
Follow the connector setup instructions in the Supermetal Console to select a source database, supermetal can optionally auto discover available source databases.
To give Supermetal access to your source database and to validate network reachability and connection credentials, you need to ensure your database's network security group allows connections from the Supermetal agent subnet.
Configure Database Access
Supermetal Console will list pre-filled steps to configure the database access as part of the prerequisite steps similar to the steps listed below.
# Set variables for your environmentRESGROUP="your-resource-group-name"CONNECTOR_NAME="connector1" # Unique identifier for this connectorNSG_NAME="supermetal-${CONNECTOR_NAME}-nsg" # Unique NSG name for this connectorDB_NSG_NAME="your-db-nsg-name"VNET_NAME="your-vnet-name"AGENT_SUBNET_NAME="your-agent-subnet-name" # Subnet where Supermetal agents will be deployedLOCATION="eastus2"DB_PORT=5432 # For PostgreSQLSUPERMETAL_METADATA_ENDPOINT="Provided-By-Supermetal" # Specific endpoint for Supermetal metadata service# Create the NSG for Supermetal agentsaz network nsg create \ --resource-group $RESGROUP \ --name $NSG_NAME \ --location $LOCATION# Add outbound rule for metadata private endpoint accessaz network nsg rule create \ --resource-group $RESGROUP \ --nsg-name $NSG_NAME \ --name "AllowMetadataAccess" \ --priority 100 \ --direction "Outbound" \ --access "Allow" \ --protocol "Tcp" \ --destination-port-ranges 443 \ --destination-address-prefixes $SUPERMETAL_METADATA_ENDPOINT \ --description "Allow access to Supermetal metadata service"# Add outbound rule for database accessaz network nsg rule create \ --resource-group $RESGROUP \ --nsg-name $NSG_NAME \ --name "AllowDatabaseAccess" \ --priority 110 \ --direction "Outbound" \ --access "Allow" \ --protocol "Tcp" \ --destination-port-ranges $DB_PORT \ --destination-address-prefixes "YOUR_DATABASE_SUBNET_PREFIX" \ --description "Allow access to database"# Add inbound rule to database NSG to allow access from Supermetal agentaz network nsg rule create \ --resource-group $RESGROUP \ --nsg-name $DB_NSG_NAME \ --name "AllowSupermetalAgentAccess" \ --priority 100 \ --direction "Inbound" \ --access "Allow" \ --protocol "Tcp" \ --source-address-prefixes "YOUR_AGENT_SUBNET_PREFIX" \ --destination-port-ranges $DB_PORT \ --description "Allow Supermetal agent access to database"# Associate the NSG with the agent subnetaz network vnet subnet update \ --resource-group $RESGROUP \ --vnet-name $VNET_NAME \ --name $AGENT_SUBNET_NAME \ --network-security-group $NSG_NAME
Parameters
RESGROUP: Your Azure resource group name where the NSGs will be created
CONNECTOR_NAME: A unique identifier for this connector (important if you're deploying multiple connectors)
VNET_NAME: The name of your Azure Virtual Network
AGENT_SUBNET_NAME: The name of the subnet where Supermetal agents will be deployed
YOUR_DATABASE_SUBNET_PREFIX: CIDR range of your database subnet (e.g., "10.0.2.0/24")
YOUR_AGENT_SUBNET_PREFIX: CIDR range of the subnet where Supermetal agents will be deployed
DB_PORT: The port number your database listens on (e.g., 5432 for PostgreSQL)
SUPERMETAL_METADATA_ENDPOINT: Specific endpoint for the Supermetal metadata service (provided by Supermetal)
Follow the setup instructions in the Supermetal Console to input the created NSG ID for the source database and validate the network reachability and connection credentials.
Once the source and target database connections are validated, you can finalize the connector setup which will launch the Supermetal agents in your Azure VNet.
Supermetal provides Terraform scripts for bootstrapping your Azure environment.
The scripts create all necessary resources and permissions for Supermetal to manage the data replication agents in your Azure subscription.
terraform { required_version = ">= 1.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } azuread = { source = "hashicorp/azuread" version = "~> 2.0" } }}provider "azurerm" { features { key_vault { purge_soft_delete_on_destroy = false recover_soft_deleted_key_vaults = true } }}provider "azuread" {}# =============================================================================# Data Sources# =============================================================================data "azurerm_client_config" "current" {}data "azurerm_subscription" "current" {}# =============================================================================# Input Variables# =============================================================================variable "resource_group_name" { description = "Name of the Resource Group where Supermetal will deploy resources" type = string validation { condition = can(regex("^[a-zA-Z0-9-_]+$", var.resource_group_name)) error_message = "Resource group name must contain only alphanumeric characters, hyphens, and underscores." }}variable "location" { description = "Azure region for deployment (e.g., eastus, westeurope)" type = string}variable "vnet_id" { description = "Resource ID of your existing Virtual Network (e.g., /subscriptions/.../resourceGroups/.../providers/Microsoft.Network/virtualNetworks/...)" type = string}variable "private_subnet_ids" { description = "List of private subnet Resource IDs where agents and private endpoints will be deployed" type = list(string) validation { condition = length(var.private_subnet_ids) > 0 error_message = "At least one private subnet must be provided." }}variable "credentials_key_vault_id" { description = "Resource ID of existing Key Vault for storing credentials. If empty, a new Key Vault will be created" type = string default = ""}variable "external_id" { description = "Unique identifier provided by Supermetal Console for securing cross-tenant access" type = string sensitive = true}variable "supermetal_tenant_id" { description = "Azure AD Tenant ID of Supermetal Control Plane" type = string}variable "supermetal_app_id" { description = "Azure AD Application ID of Supermetal Control Plane" type = string}variable "supermetal_principal_id" { description = "Object ID of Supermetal's Service Principal" type = string}variable "supermetal_metadata_service_id" { description = "Resource ID of Supermetal Metadata Service's Private Link Service" type = string}variable "supermetal_container_registry_name" { description = "Name of Supermetal's container registry for agent images" type = string}# =============================================================================# Local Values# =============================================================================locals { # Parse VNet information vnet_parts = split("/", var.vnet_id) vnet_name = element(local.vnet_parts, length(local.vnet_parts) - 1) vnet_resource_group = element(local.vnet_parts, index(local.vnet_parts, "resourceGroups") + 1) # Subnet selection primary_subnet_id = var.private_subnet_ids[0] # Key Vault configuration create_key_vault = var.credentials_key_vault_id == "" key_vault_id = local.create_key_vault ? azurerm_key_vault.credentials[0].id : var.credentials_key_vault_id key_vault_name = local.create_key_vault ? "supermetal-kv-${substr(md5(var.resource_group_name), 0, 8)}" : "" existing_kv_rg = local.create_key_vault ? "" : split("/", var.credentials_key_vault_id)[4] existing_kv_name = local.create_key_vault ? "" : split("/", var.credentials_key_vault_id)[8] # Common tags common_tags = { "supermetal:byoc:external-id" = var.external_id "supermetal:byoc:resource-group" = var.resource_group_name "supermetal:byoc:stack-name" = "supermetal-bootstrap-${var.resource_group_name}" "supermetal:managed" = "true" "Environment" = "Production" }}# =============================================================================# Resource Group Data Source# =============================================================================data "azurerm_resource_group" "target" { name = var.resource_group_name}# =============================================================================# Networking Data Sources# =============================================================================data "azurerm_virtual_network" "vnet" { name = local.vnet_name resource_group_name = local.vnet_resource_group}data "azurerm_subnet" "primary" { name = element(split("/", local.primary_subnet_id), length(split("/", local.primary_subnet_id)) - 1) virtual_network_name = local.vnet_name resource_group_name = local.vnet_resource_group}# =============================================================================# Custom Role Definition for Supermetal Control Plane# =============================================================================resource "azurerm_role_definition" "supermetal_control_plane" { name = "SupermetalControlPlaneRole-${var.resource_group_name}" scope = data.azurerm_subscription.current.id description = "Allows Supermetal Control Plane to manage resources in customer subscription" permissions { actions = [ # ===== Virtual Machine Scale Sets ===== "Microsoft.Compute/virtualMachineScaleSets/read", "Microsoft.Compute/virtualMachineScaleSets/write", "Microsoft.Compute/virtualMachineScaleSets/delete", "Microsoft.Compute/virtualMachineScaleSets/start/action", "Microsoft.Compute/virtualMachineScaleSets/powerOff/action", "Microsoft.Compute/virtualMachineScaleSets/restart/action", "Microsoft.Compute/virtualMachineScaleSets/deallocate/action", "Microsoft.Compute/virtualMachineScaleSets/manualUpgrade/action", "Microsoft.Compute/virtualMachineScaleSets/scale/action", "Microsoft.Compute/virtualMachineScaleSets/instanceView/read", "Microsoft.Compute/virtualMachineScaleSets/skus/read", # ===== VMSS Extensions (for Docker/Agent setup) ===== "Microsoft.Compute/virtualMachineScaleSets/extensions/read", "Microsoft.Compute/virtualMachineScaleSets/extensions/write", "Microsoft.Compute/virtualMachineScaleSets/extensions/delete", # ===== Virtual Machines (VMSS instances) ===== "Microsoft.Compute/virtualMachines/read", "Microsoft.Compute/virtualMachines/instanceView/read", # ===== Managed Disks ===== "Microsoft.Compute/disks/read", "Microsoft.Compute/disks/write", "Microsoft.Compute/disks/delete", # ===== Container Instances (for validation) ===== "Microsoft.ContainerInstance/containerGroups/read", "Microsoft.ContainerInstance/containerGroups/write", "Microsoft.ContainerInstance/containerGroups/delete", "Microsoft.ContainerInstance/containerGroups/restart/action", "Microsoft.ContainerInstance/containerGroups/stop/action", "Microsoft.ContainerInstance/containerGroups/start/action", "Microsoft.ContainerInstance/containerGroups/containers/logs/read", "Microsoft.ContainerInstance/containerGroups/containers/exec/action", # ===== Container Registry Access ===== "Microsoft.ContainerRegistry/registries/pull/read", "Microsoft.ContainerRegistry/registries/artifacts/read", "Microsoft.ContainerRegistry/registries/metadata/read", # ===== Storage Accounts ===== "Microsoft.Storage/storageAccounts/read", "Microsoft.Storage/storageAccounts/write", "Microsoft.Storage/storageAccounts/delete", "Microsoft.Storage/storageAccounts/listkeys/action", "Microsoft.Storage/storageAccounts/listAccountSas/action", "Microsoft.Storage/storageAccounts/blobServices/containers/read", "Microsoft.Storage/storageAccounts/blobServices/containers/write", "Microsoft.Storage/storageAccounts/blobServices/containers/delete", # ===== Networking ===== "Microsoft.Network/virtualNetworks/subnets/join/action" "Microsoft.Network/networkInterfaces/read" "Microsoft.Network/networkSecurityGroups/read" "Microsoft.Network/networkSecurityGroups/join/action" # ===== Key Vault Management ===== "Microsoft.KeyVault/vaults/read", # ===== Database Discovery (Read-only) - Scoped to resource group ===== "Microsoft.Sql/servers/read", "Microsoft.Sql/servers/databases/read", "Microsoft.DBforPostgreSQL/servers/read", "Microsoft.DBforPostgreSQL/flexibleServers/read", "Microsoft.DBforMySQL/servers/read", "Microsoft.DBforMySQL/flexibleServers/read", "Microsoft.DBforMariaDB/servers/read" ] not_actions = [] data_actions = [ # ===== Key Vault Data Operations (scoped to supermetal-* items) ===== "Microsoft.KeyVault/vaults/secrets/getSecret/action", "Microsoft.KeyVault/vaults/secrets/setSecret/action", "Microsoft.KeyVault/vaults/keys/encrypt/action", "Microsoft.KeyVault/vaults/keys/decrypt/action", # ===== Storage Data Operations ===== "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read", "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write", "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/delete", "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/add/action" ] not_data_actions = [] } assignable_scopes = [ data.azurerm_resource_group.target.id ]}# =============================================================================# Role Assignment for Supermetal Control Plane# =============================================================================resource "azurerm_role_assignment" "supermetal_control_plane" { scope = data.azurerm_resource_group.target.id role_definition_id = azurerm_role_definition.supermetal_control_plane.role_definition_resource_id principal_id = var.supermetal_principal_id principal_type = "ServicePrincipal" # Condition to validate this is the correct service principal condition = <<-EOT ( @Request[Microsoft.Authorization/roleAssignments:principalId] == '${var.supermetal_principal_id}' ) AND ( @Request[Microsoft.Authorization/roleAssignments:principalType] == 'ServicePrincipal' ) EOT condition_version = "2.0" description = "Supermetal Control Plane access with External ID: ${var.external_id}"}# =============================================================================# User-Assigned Managed Identities# =============================================================================# Identity for Supermetal Agents (VMSS)resource "azurerm_user_assigned_identity" "agent" { name = "supermetal-agent-identity-${var.resource_group_name}" location = var.location resource_group_name = var.resource_group_name tags = local.common_tags}# Identity for Validation Containers (ACI)resource "azurerm_user_assigned_identity" "validation" { name = "supermetal-validation-identity-${var.resource_group_name}" location = var.location resource_group_name = var.resource_group_name tags = local.common_tags}# =============================================================================# Key Vault for Credentials# =============================================================================resource "azurerm_key_vault" "credentials" { count = local.create_key_vault ? 1 : 0 name = local.key_vault_name location = var.location resource_group_name = var.resource_group_name tenant_id = data.azurerm_client_config.current.tenant_id sku_name = "standard" soft_delete_retention_days = 90 purge_protection_enabled = true enable_rbac_authorization = false network_acls { default_action = "Deny" bypass = "AzureServices" ip_rules = [] virtual_network_subnet_ids = var.private_subnet_ids } tags = local.common_tags}# =============================================================================# Key Vault Access Policies# =============================================================================# Access for Supermetal Control Plane (encrypt only)resource "azurerm_key_vault_access_policy" "control_plane" { key_vault_id = local.key_vault_id tenant_id = var.supermetal_tenant_id object_id = var.supermetal_principal_id key_permissions = [ "Get", "List", "Encrypt" ] secret_permissions = [ "Set", "List" ]}# Access for Agent Identity (decrypt)resource "azurerm_key_vault_access_policy" "agent" { key_vault_id = local.key_vault_id tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_user_assigned_identity.agent.principal_id key_permissions = [ "Get", "Decrypt" ] secret_permissions = [ "Get" ]}# Access for Validation Identityresource "azurerm_key_vault_access_policy" "validation" { key_vault_id = local.key_vault_id tenant_id = data.azurerm_client_config.current.tenant_id object_id = azurerm_user_assigned_identity.validation.principal_id key_permissions = [ "Get", "Decrypt" ] secret_permissions = [ "Get" ]}# =============================================================================# Network Security Groups# =============================================================================# NSG for Agent Metadata Accessresource "azurerm_network_security_group" "agent_metadata" { name = "supermetal-agent-metadata-nsg-${var.resource_group_name}" location = var.location resource_group_name = var.resource_group_name security_rule { name = "AllowHttpsToMetadataEndpoint" priority = 100 direction = "Outbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "443" source_address_prefix = "VirtualNetwork" destination_address_prefix = "PrivateEndpoint" description = "Allow HTTPS traffic to Supermetal metadata private endpoint" } security_rule { name = "AllowAzureStorage" priority = 110 direction = "Outbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "443" source_address_prefix = "VirtualNetwork" destination_address_prefix = "Storage.${var.location}" description = "Allow access to Azure Storage for buffer operations" } security_rule { name = "AllowAzureKeyVault" priority = 120 direction = "Outbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "443" source_address_prefix = "VirtualNetwork" destination_address_prefix = "AzureKeyVault.${var.location}" description = "Allow access to Azure Key Vault for credential retrieval" } tags = local.common_tags}# =============================================================================# Private Endpoint for Metadata Service# =============================================================================resource "azurerm_private_endpoint" "metadata" { name = "supermetal-metadata-endpoint-${var.resource_group_name}" location = var.location resource_group_name = var.resource_group_name subnet_id = local.primary_subnet_id private_service_connection { name = "supermetal-metadata-connection" private_connection_resource_id = var.supermetal_metadata_service_id is_manual_connection = false subresource_names = ["metadata"] } tags = local.common_tags}# =============================================================================# Container Registry Access Role Assignments# =============================================================================# Role assignment for agent identity to pull imagesresource "azurerm_role_assignment" "agent_acr_pull" { scope = "/subscriptions/${var.supermetal_tenant_id}/resourceGroups/supermetal-control-plane/providers/Microsoft.ContainerRegistry/registries/${var.supermetal_container_registry_name}" role_definition_name = "AcrPull" principal_id = azurerm_user_assigned_identity.agent.principal_id principal_type = "ServicePrincipal"}# Role assignment for validation identity to pull imagesresource "azurerm_role_assignment" "validation_acr_pull" { scope = "/subscriptions/${var.supermetal_tenant_id}/resourceGroups/supermetal-control-plane/providers/Microsoft.ContainerRegistry/registries/${var.supermetal_container_registry_name}" role_definition_name = "AcrPull" principal_id = azurerm_user_assigned_identity.validation.principal_id principal_type = "ServicePrincipal"}# =============================================================================# Outputs# =============================================================================output "control_plane_role_definition_id" { description = "ID of the custom role definition for Supermetal Control Plane" value = azurerm_role_definition.supermetal_control_plane.role_definition_resource_id}output "control_plane_role_assignment_id" { description = "ID of the role assignment for Supermetal Control Plane" value = azurerm_role_assignment.supermetal_control_plane.id}output "agent_identity_resource_id" { description = "Resource ID of the Managed Identity for Supermetal agents" value = azurerm_user_assigned_identity.agent.id}output "agent_identity_client_id" { description = "Client ID of the Managed Identity for Supermetal agents" value = azurerm_user_assigned_identity.agent.client_id}output "agent_identity_principal_id" { description = "Principal ID of the Managed Identity for Supermetal agents" value = azurerm_user_assigned_identity.agent.principal_id}output "validation_identity_resource_id" { description = "Resource ID of the Managed Identity for validation containers" value = azurerm_user_assigned_identity.validation.id}output "validation_identity_client_id" { description = "Client ID of the Managed Identity for validation containers" value = azurerm_user_assigned_identity.validation.client_id}output "key_vault_id" { description = "Resource ID of the Key Vault for credentials" value = local.key_vault_id}output "key_vault_uri" { description = "URI of the Key Vault for credentials" value = local.create_key_vault ? azurerm_key_vault.credentials[0].vault_uri : "https://${local.existing_kv_name}.vault.azure.net/"}output "metadata_endpoint_id" { description = "Resource ID of the Private Endpoint for Supermetal Metadata Service" value = azurerm_private_endpoint.metadata.id}output "metadata_endpoint_ip" { description = "Private IP address of the Metadata Service endpoint" value = azurerm_private_endpoint.metadata.private_service_connection[0].private_ip_address}output "agent_metadata_nsg_id" { description = "Resource ID of the NSG for agent metadata access" value = azurerm_network_security_group.agent_metadata.id}output "bootstrap_timestamp" { description = "Timestamp when bootstrap was completed" value = timestamp()}
Bootstrap creates several Azure roles and managed identities with specific permissions necessary for Supermetal BYOC to function securely.
These roles ensure that the Supermetal Control Plane can manage resources in your Azure subscription on your behalf, and that the Supermetal agents have the necessary permissions to operate.
The principle of least privilege is applied to scope down permissions as much as possible.
This role is assigned to the Supermetal Service Principal. It enables the Supermetal Control Plane to orchestrate and manage the lifecycle of resources required for the Supermetal data plane within your Azure subscription.
Virtual Machine Scale Sets Management
Create, read, update, and delete VMSS resources (Microsoft.Compute/virtualMachineScaleSets/*)
Control instance lifecycle (start, stop, restart, scale) for agent deployment
Manage VMSS extensions for Docker/agent setup
Read-only access to VM instances (Microsoft.Compute/virtualMachines/read)
Container Instances and Registry
Full management of Container Instances for validation tasks (Microsoft.ContainerInstance/containerGroups/*)
Pull access to container registry for agent images (Microsoft.ContainerRegistry/registries/pull/read)
This User-Assigned Managed Identity is attached to the VMSS instances running Supermetal agents. It provides the following permissions:
Key Vault Access
Get and decrypt operations on keys (Get, Decrypt)
Retrieve secrets from Key Vault (Get)
Used to securely access connection credentials
Container Registry Access
AcrPull role on Supermetal's container registry
Enables pulling agent container images from Supermetal's registry
This User-Assigned Managed Identity is used by Azure Container Instances (ACI) that may be deployed for validation tasks such as testing database connectivity or network reachability.
Key Vault Access
Similar permissions to the Agent identity (Get, Decrypt)
Retrieves necessary connection credentials for validation