# MPC deployment on Azure

This guide covers deploying customer MPC nodes (Node 2 and Node 3) to Microsoft Azure using the **MPC Deployer** tool.

**Prerequisites:** Review the [MPC overview](/products/custody/v1.36/deployment/integrate-kms/mpc/overview) to understand the architecture, shared responsibility model, and node ownership before proceeding.

## Overview

The MPC Deployer automates the deployment of customer MPC nodes to Azure Confidential VMs, using AMD SEV-SNP based isolation for node execution and Azure Key Vault for node key material and deployment configuration.

### What gets deployed


```mermaid
flowchart TB
    subgraph azure["Azure Subscription (Customer)"]
        subgraph rg["Resource Group"]
            subgraph vnet["Virtual Network"]
                subgraph subnet1["Subnet 1"]
                    cvm1["Confidential VM<br/>MPC Node 2"]
                end
                subgraph subnet2["Subnet 2"]
                    cvm2["Confidential VM<br/>MPC Node 3"]
                end
            end
            kv["Deployer Key Vault<br/>MPC Nodes Keys & Deployer config"]
            hsmkv["Premium Key Vault<br/>Contains the enclave contract wrapping key"]
            mi["User-Assigned<br/>Managed Identity"]
            storage["Storage Account<br/>Terraform State"]
            logs["Boot diagnostics and Log Analytics Workspace (LAW)"]
        end
    end

    cvm1 --> kv
    cvm2 --> kv
    cvm1 --> hsmkv
    cvm2 --> hsmkv
    mi --> kv
    mi --> hsmkv
    cvm1 --> logs
    cvm2 --> logs

    style azure fill:#e3f2fd,stroke:#1565c0
    style rg fill:#fafafa,stroke:#616161
    style vnet fill:#e8f5e9,stroke:#2e7d32
    style cvm1 fill:#fff3e0,stroke:#ef6c00
    style cvm2 fill:#fff3e0,stroke:#ef6c00
    style kv fill:#f3e5f5,stroke:#6a1b9a
    style hsmkv fill:#fce4ec,stroke:#ad1457
```

**Existing VNet support:** The deployer supports a mode where networking resources are not created. Use the `--no-create-vpc` flag if your organization manages Azure networking centrally.

### Azure resources

#### Pre-provisioned resources

Azure deployments require several resources to exist before you run the deployer:

| Resource | Purpose | Notes |
|  --- | --- | --- |
| Resource group | Deployment boundary for Azure resources | Required |
| Standard Key Vault | Stores MPC Nodes Keys & Deployer config | Must use RBAC authorization |
| Premium Key Vault | Holds the enclave contract wrapping key | Must contain an RSA-4096 HSM-backed key |
| User-assigned managed identity | Allows Confidential VMs to access Key Vault resources | Must be created before deployment; grant access yourself or allow the deployer to assign Key Vault roles |
| Existing VNet and subnets | Customer-managed networking | Required only with `--no-create-vpc` |


Ripple can share utility scripts with you to ease the creation of these pre-provisioned resources within your Azure environment.

#### Resources created by the deployer

| Resource | Count | Purpose | Notes |
|  --- | --- | --- | --- |
| Virtual Network | 1 | Network isolation | Optional - use `--no-create-vpc` to skip |
| Subnets | 2 | MPC node hosting | Optional - not created with `--no-create-vpc` |
| Confidential VMs | 2 | MPC Node 2 and Node 3 | Required |
| Network interfaces | 2 | VM network attachment | Required |
| Network security groups | 2 | Network access control | Required |
| Storage account/container | 1 | Terraform state storage, contract token hashes | Required |
| Key Vault secrets/configuration | Multiple | Keys, KEKs, and deployment configuration | Required |
| Boot diagnostics and Log Analytics Workspace (LAW) | 2 | Startup and node troubleshooting | Required or customer-configured |


## Prerequisites

### Azure requirements

**Subscription and permissions:**

- Azure subscription dedicated to Ripple MPC Nodes deployment.
- Permissions allowing the deployer tool to create resources in the Azure subscription.
- Confidential VM quota and supported VM SKUs in the selected Azure region.


**Credentials:**

Authenticate to Azure using the method approved by your organization. For service-principal based deployments, provide Azure credentials to the container using the environment variables expected by the deployer and the Terraform Azure provider, for example:


```bash
export ARM_SUBSCRIPTION_ID="<subscription-id>"
export ARM_TENANT_ID="<tenant-id>"
export ARM_CLIENT_ID="<service-principal-client-id>"
export ARM_CLIENT_SECRET="<service-principal-client-secret>"
```

### Execution requirements

- **Docker**: Docker Desktop or Docker Engine installed and running.
- **MPC Deployer Image**: Access to Ripple's Docker registry.


**Important:** Contact your Ripple representative to obtain the official MPC Deployer Docker image and required credentials.

### From Ripple

Before deployment, Ripple will provide:

| Item | Description |
|  --- | --- |
| **MPC Deployer Image** | Official Docker image tag and registry credentials |
| **Tenant Alias** | Your organization identifier |
| **Tenant Instance Code** | Environment identifier, such as `prod` or `uat` |
| **Docker Registry Credentials** | Access to `metaco.azurecr.io`, where MPC core component images are stored |
| **OpenTelemetry Endpoint** | Observability service configuration |
| **Azure setup scripts** | Optional scripts for pre-provisioning required Azure resources |


## Deployment process

The deployment follows a coordinated process between you and Ripple:


```mermaid
sequenceDiagram
    participant C as Customer
    participant T as MPC Deployer Tool
    participant AZ as Azure Subscription
    participant R as Ripple

    Note over C,AZ: Phase 0: Azure Prerequisites
    C->>AZ: Pre-provision Resource Group, Key Vaults, contract wrapping key, Log Analytics Workspace, and Managed Identity
    C->>T: Provide Azure input values

    Note over C,R: Phase 1: Key Generation
    C->>T: Run --keygen mode
    T->>AZ: Store keys in Azure Key Vault
    T->>C: Output Node 2 & 3 Peer IDs
    C->>R: Share Peer IDs

    Note over C,R: Phase 2: Ripple Setup
    R->>R: Configure Nodes 0 & 1
    R->>R: Configure Relay Server
    R->>C: Provide Ripple config

    Note over C,R: Phase 3: Deployment
    C->>T: Run deployment mode
    T->>AZ: Provision infrastructure
    T->>AZ: Deploy Azure Confidential VMs
    T->>C: Deployment complete

    Note over C,R: Phase 4: Verification
    C->>R: Confirm connectivity
    R->>C: Run cluster health check
```

## Step 1: Pre-provision Azure resources

Before running the deployer, confirm that the following resources exist:

| Requirement | What to verify |
|  --- | --- |
| Resource group | The target resource group exists in the primary Azure region |
| Standard Key Vault | RBAC authorization is enabled and the deployer identity can read and write secrets |
| Premium Key Vault | The vault contains an RSA-4096 HSM-backed wrapping key |
| User-assigned managed identity | The identity exists and can be assigned to the Confidential VMs |
| Key Vault access | The managed identity has access to both Key Vaults, or the deployer identity can create the required role assignments |
| Network | Either allow the deployer to create the VNet and subnets, or provide two existing subnets |
| Outbound connectivity | Subnets allow outbound HTTPS and connectivity to the Ripple relay host and port |


## Step 2: Generate node keys

Create an `.env` file using the sample provided with your MPC Deployer package. For key generation, include your tenant details, Azure region, and the pre-provisioned Azure resource values.

At minimum, the key-generation configuration includes:

| Value | Description |
|  --- | --- |
| `REGION` | Primary Azure region |
| `TENANT_ALIAS` | Your organization identifier |
| `TENANT_INSTANCE_CODE` | Environment code |
| `AZURE_SUBSCRIPTION_ID` | Azure subscription ID |
| `AZURE_RESOURCE_GROUP` | Resource group used by the deployment |
| `AZURE_KEY_VAULT_NAME` | Key Vault used for node keys, configuration, and contract tokens |


Generate cryptographic keys for your MPC nodes:


```bash
docker run --rm -it \
  --env-file .env \
  -e ARM_SUBSCRIPTION_ID \
  -e ARM_TENANT_ID \
  -e ARM_CLIENT_ID \
  -e ARM_CLIENT_SECRET \
  -v $(pwd)/output:/app/output \
  <RIPPLE_REGISTRY>/mpc-deployer:<VERSION> \
  --provider azure --keygen --output /app/output --non-interactive
```

The tool stores the private node keys and KEKs in Azure Key Vault. The output includes the public Peer IDs for Node 2 and Node 3:


```text
Key material successfully generated and stored in Azure Key Vault.
Node 2 Public Key (Peer ID): 12D3KooWBhMevWJT...
Node 3 Public Key (Peer ID): 12D3KooWCdEfGhIj...
```

**Share with Ripple:** Send the Node 2 and Node 3 Peer IDs to your Ripple representative. These are public identifiers and are safe to share.

## Step 3: Wait for Ripple configuration

Ripple will configure their infrastructure and provide you with:

| Configuration | Example |
|  --- | --- |
| **Relay Host** | `relay.mpc.ripple.com` |
| **Relay Port** | `6000` |
| **Relay Public Key** | `12D3KooW...` |
| **Node 0 Public Key** | `12D3KooW...` |
| **Node 1 Public Key** | `12D3KooW...` |
| **MPC Docker Image** | `metaco.azurecr.io/harmonize/mpc-core:1.13.12` |


Add the following values to your `.env` file before deployment:

| Value | Description |
|  --- | --- |
| `MPC_DOCKER_IMAGE` | MPC core Docker image provided by Ripple |
| `MPC_DOCKER_REGISTRY_USERNAME` | Docker registry username |
| `MPC_DOCKER_REGISTRY_PASSWORD` | Docker registry password |
| `MPC_RELAY_HOST` | Relay server host |
| `MPC_RELAY_PORT` | Relay server port |
| `MPC_RELAY_PUBLIC_KEY` | Relay server public key |
| `MPC_NODE0_PUBLIC_KEY` | Node 0 public key |
| `MPC_NODE1_PUBLIC_KEY` | Node 1 public key |
| `AZURE_NSG_RIPPLE_SAAS_CIDRS` | CIDR blocks allowed by the network security groups for Ripple SaaS connectivity |
| `AZURE_NSG_DOCKER_REGISTRY_CIDRS` | CIDR blocks allowed by the network security groups for Docker registry access |
| `OTEL_HTTP_ENDPOINT` | OpenTelemetry endpoint |
| `OTEL_HTTP_USER` | OpenTelemetry username |
| `OTEL_HTTP_PASSWORD` | OpenTelemetry password |
| `AZURE_WRAPPING_KEY_VAULT_NAME` | Premium Key Vault that holds the contract wrapping key (Azure SKR) |
| `AZURE_KEY_WRAPPING_KEY_NAME` | Name of the RSA-4096 HSM-backed contract wrapping key |
| `RIPPLE_COSIGN_PUBLIC_KEY` | Cosign public key that verifies mpc-core image signatures at CVM boot |


## Step 4: Deploy MPC nodes

After Ripple provides the cluster configuration, deploy Node 2 and Node 3 to Azure.

### Non-interactive deployment (recommended)

For production deployments, use environment variables with the `--non-interactive` flag:


```bash
docker run --rm -it \
  --env-file .env \
  -e ARM_SUBSCRIPTION_ID \
  -e ARM_TENANT_ID \
  -e ARM_CLIENT_ID \
  -e ARM_CLIENT_SECRET \
  -v $(pwd)/output:/app/output \
  <RIPPLE_REGISTRY>/mpc-deployer:<VERSION> \
  --provider azure --output /app/output --non-interactive
```

### Interactive deployment (fallback)

If you prefer to enter configuration interactively:


```bash
docker run --rm -it \
  --env-file .env \
  -e ARM_SUBSCRIPTION_ID \
  -e ARM_TENANT_ID \
  -e ARM_CLIENT_ID \
  -e ARM_CLIENT_SECRET \
  -v $(pwd)/output:/app/output \
  <RIPPLE_REGISTRY>/mpc-deployer:<VERSION> \
  --provider azure --output /app/output
```

### Deployment into an existing VNet

If you have existing subnets, use the `--no-create-vpc` flag:


```bash
docker run --rm -it \
  --env-file .env \
  -e ARM_SUBSCRIPTION_ID \
  -e ARM_TENANT_ID \
  -e ARM_CLIENT_ID \
  -e ARM_CLIENT_SECRET \
  -v $(pwd)/output:/app/output \
  <RIPPLE_REGISTRY>/mpc-deployer:<VERSION> \
  --provider azure --output /app/output --non-interactive --no-create-vpc
```

Your `.env` file must include the existing VNet and subnet values required by your deployer package. The two subnets should be in separate availability zones or fault domains when your Azure region and networking design support it.

| Value | Description |
|  --- | --- |
| `AZURE_PRECONFIGURED_PRIVATE_SUBNET1_ID` | Existing subnet ID for MPC Node 2 |
| `AZURE_PRECONFIGURED_PRIVATE_SUBNET2_ID` | Existing subnet ID for MPC Node 3 |


## Step 5: Verify deployment

After deployment completes, verify the Azure infrastructure and node startup.

### Check Confidential VMs

Verify that both VMs are running:


```bash
az vm list \
  --resource-group <RESOURCE_GROUP> \
  --show-details \
  --query "[?contains(name, 'mpc')].{Name:name,PowerState:powerState,Size:hardwareProfile.vmSize}" \
  --output table
```

**What to check:** Confirm both MPC VMs show `VM running` and use the Confidential VM SKU selected for your deployment.

### Check managed identity assignment

Confirm that each VM has the expected user-assigned managed identity:


```bash
az vm identity show \
  --resource-group <RESOURCE_GROUP> \
  --name <MPC_VM_NAME>
```

### Check boot diagnostics

Retrieve the startup log for each VM:


```bash
az vm boot-diagnostics get-boot-log \
  --resource-group <RESOURCE_GROUP> \
  --name <MPC_VM_NAME>
```

**What to check:** Look for log entries indicating that the node retrieved its configuration from Key Vault and connected to the relay server.

### Verify Key Vault entries

Confirm that deployment secrets exist in the Standard Key Vault:


```bash
az keyvault secret list \
  --vault-name <CONFIG_KEY_VAULT_NAME> \
  --query "[?contains(name, '<TENANT_ALIAS>')].name"
```

**What to check:** This command lists secret names only and does not reveal key material. Verify that secrets for Node 2 and Node 3 exist.

## Command reference

| Flag | Description | Default |
|  --- | --- | --- |
| `--output` | Output directory for logs and generated files | `./output/` |
| `--keygen` | Generate key material for customer nodes | `false` |
| `--no-create-vpc` | Use existing Azure networking instead of creating a VNet and subnets | `false` |
| `--non-interactive` | Disable interactive prompts, recommended for production | `false` |
| `--dry-run` | Simulate deployment without making changes | `false` |


## Troubleshooting

### Common issues

| Issue | Cause | Solution |
|  --- | --- | --- |
| Azure credentials error | Missing, expired, or unauthorized credentials | Refresh Azure credentials and verify subscription access |
| Key Vault access denied | Managed identity or deployer identity lacks RBAC permissions | Grant the required Key Vault roles before rerunning |
| Contract wrapping key not found | Premium Key Vault or RSA-4096 HSM key is missing or misnamed | Verify the Premium Key Vault name and key name |
| Confidential VM launch failed | VM SKU unavailable, quota exceeded, or region unsupported | Choose a supported region/SKU or request quota |
| VM cannot retrieve configuration | Managed identity is not assigned or cannot access Key Vault | Check VM identity assignment and Key Vault RBAC |
| Docker registry auth failed | Invalid registry credentials | Verify credentials with Ripple |
| Relay connection failed | Network blocks outbound relay traffic | Confirm subnet routing, NSG rules, and proxy configuration |
| Role assignment failed | Deployer identity cannot create role assignments | Grant the required Key Vault role assignments and rerun the deployment |


### Log files

All deployer operations are logged to the output directory:

| Log File | Content |
|  --- | --- |
| `output.log` | Main application logs |
| `cdktf.log` | Infrastructure provisioning logs |


### Getting help

If you encounter issues:

1. Check log files in the output directory.
2. Verify Azure credentials, Key Vault access, and managed identity assignment.
3. Retrieve VM boot diagnostics for Node 2 and Node 3.
4. Contact your Ripple representative with log files and boot diagnostics.


## Next steps

After successful deployment, contact your Ripple representative to:

1. **Verify connectivity** between your nodes and Ripple nodes.
2. **Complete cluster configuration** and run health checks.
3. **Test key generation** using the DKG process.
4. **Set up [MPC backups](/products/custody/v1.36/deployment/integrate-kms/mpc/backups)** for encrypted shard backups with verifiable encryption.