# Azure Deployment with Terraform

This guide describes how the BlueRock Secure MCP Server is deployed on Microsoft Azure using Terraform.

Terraform scripts are executed from a local machine or CI/CD environment with the Microsoft Azure CLI (`az`) initialized. The scripts utilize the Azure Resource Manager APIs to provision the necessary Compute, Networking, Storage, and Monitor resources.

***

### Prerequisites

* Microsoft Azure CLI tools (`az`), for installation refer to the official [Microsoft Azure CLI installation guide](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest).
* [Terraform](https://developer.hashicorp.com/terraform/install): Version 1.0 or higher installed.
* BlueRock images: BlueRock provides pre-packaged images of BlueRock Ubuntu 2404 Linux Distribution - (Free or Full version), contact [BlueRock Support](https://www.bluerock.io/contact).

| Version | OS           | Kernel Version | Image Name                                       | Description                                                                                             |
| ------- | ------------ | -------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| Free    | Ubuntu 24.04 | 6.12.63        | bluerock-release-26-08-2-ubuntu2404-6.12.63-free | Loads default policy in observe mode and policy changes are not allowed.                                |
| Full    | Ubuntu 24.04 | 6.12.63        | bluerock-release-26-08-2-ubuntu2404-6.12.63      | Provides full policy configuration control. Allow switching policy action from observe to enforce mode. |

#### Prerequisite Steps

Execute the following steps prior to initiating a Terraform deployment to provision the BlueRock "Golden Image" within the customer's Azure Compute Gallery.

1. **Set Environment Variables**:\
   Update the variables below with target environment details and execute the block in the terminal. Refer to the provided table for parameter definitions:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">CUSTOMER_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
   CUSTOMER_RG="rg-customer-sec"
   CUSTOMER_GALLERY="gal_customer_bluerock"
   IMAGE_DEF="bluerock-golden-linux"
   IMAGE_VER="1.0.4"
   LOCATION="eastus"
   SOURCE_IMAGE_VERSION_RESOURCE_ID="/subscriptions/&#x3C;source-sub>/resourceGroups/&#x3C;source-rg>/providers/Microsoft.Compute/galleries/&#x3C;source-gallery>/images/&#x3C;source-image-def>/versions/&#x3C;source-version>"
   </code></pre>

   | Text                               | Text                                                                             |
   | ---------------------------------- | -------------------------------------------------------------------------------- |
   | **Parameter**                      | **Description**                                                                  |
   | `CUSTOMER_SUBSCRIPTION_ID`         | Azure subscription where the customer deploys Terraform resources.               |
   | `CUSTOMER_RG`                      | Customer resource group for Compute Gallery resources.                           |
   | `CUSTOMER_GALLERY`                 | Customer Azure Compute Gallery name (customizable).                              |
   | `IMAGE_DEF`                        | Image definition name inside the customer gallery (e.g., bluerock-golden-linux). |
   | `IMAGE_VER`                        | Image version to publish/use (e.g., 1.0.4).                                      |
   | `LOCATION`                         | Azure region for gallery resources (e.g., eastus).                               |
   | `SOURCE_IMAGE_VERSION_RESOURCE_ID` | Full Azure resource ID of the source image version to copy/publish from.         |
2. **Run Provisioning Commands**:\
   Execute the following Azure CLI commands to configure the gallery and import the image versio&#x6E;*.*

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript"># Set target subscription
   $ az account set --subscription "$CUSTOMER_SUBSCRIPTION_ID"

   # Create Resource Group and Compute Gallery
   $ az group create --name "$CUSTOMER_RG" --location "$LOCATION"
   $ az sig create --resource-group "$CUSTOMER_RG" --gallery-name "$CUSTOMER_GALLERY" --location "$LOCATION"

   # Create Image Definition
   $ az sig image-definition create \
     --resource-group "$CUSTOMER_RG" --gallery-name "$CUSTOMER_GALLERY" \
     --gallery-image-definition "$IMAGE_DEF" --publisher "bluerock" \
     --offer "bluerock-golden-linux" --sku "stable" --os-type "Linux" \
     --hyper-v-generation "V2" --location "$LOCATION"

   # Create Image Version from Source
   $ az sig image-version create \
     --resource-group "$CUSTOMER_RG" --gallery-name "$CUSTOMER_GALLERY" \
     --gallery-image-definition "$IMAGE_DEF" --gallery-image-version "$IMAGE_VER" \
     --managed-image "$SOURCE_IMAGE_VERSION_RESOURCE_ID" --target-regions "$LOCATION" \
     --replica-count 1 --location "$LOCATION" \
     --query "{name:name,state:provisioningState}" -o table
   </code></pre>

{% hint style="info" icon="notes-sticky" %}
**Note:**&#x20;

The `image-definition create` step is skippable if the definition already exists.
{% endhint %}

3. **Terraform Mapping**:\
   Upon successful image provisioning, set the `boot_image` variable in the `terraform.tfvars` file to the new gallery image version ID:<br>

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">boot_image = "/subscriptions/&#x3C;CUSTOMER_SUBSCRIPTION_ID>/resourceGroups/&#x3C;CUSTOMER_RG>/providers/Microsoft.Compute/galleries/&#x3C;CUSTOMER_GALLERY>/images/&#x3C;IMAGE_DEF>/versions/&#x3C;IMAGE_VER>"
   </code></pre>

### BlueRock Azure Architecture Components

A typical Premium Public Deployment on Azure provisions the following resources:

| **Azure Resource / Service**     | **Description**                                                                                                                 |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Virtual Network (VNet) & Subnet  | Provides an isolated virtual network environment. Can be newly created or linked to an existing network.                        |
| Network Security Group (NSG)     | Controls network traffic. Default rules allow inbound SSH access (Port 22) from a specified IP CIDR.                            |
| Linux Virtual Machine            | Runs the BlueRock MCP Server using the specified hardened Ubuntu image.                                                         |
| User-Assigned Managed Identity   | Grants the VM minimal role-based access control (RBAC) permissions to interact with Blob Storage and Azure Monitor.             |
| Storage Account & Blob Container | Stores runtime configuration files and cryptographic certificates for the BlueRock node.                                        |
| Log Analytics & App Insights     | Optional external telemetry stack for collecting and monitoring system events, application logs, and OpenTelemetry (OTEL) data. |

***

#### Deployment Package Overview

**Directory Structure**

The Azure Terraform scripts follow a structured hierarchy containing the necessary definition files:

```shellscript
azure/compute/terraform/ubuntu/PublicDeployment/
├── main.tf                  # Core Azure resource definitions
├── outputs.tf               # Exported deployment variables
├── terraform.tfvars         # Active variable values (created by user)
├── terraform.tfvars.example # Template for variable configuration
├── user_data.sh.tpl         # Bootstrap script for the VM runtime
├── variables.tf             # Variable declarations and validation rules
└── README.md                # Deployment documentation
```

***

### `terraform.tfvars` Parameter Reference

The following parameters are defined in the `variables.tf` file. These values must be updated in the local `terraform.tfvars` file to match the target Azure project environment before executing the deployment scripts.

| **Parameter**                                 | **Description**                                                                                    | **Required** | **Example / Default Value**              |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------ | ---------------------------------------- |
| `subscription_id`                             | The unique Azure subscription ID used for deployment.                                              | Yes          | `11111111-2222-3333-4444-555555555555`   |
| `location`                                    | Azure region where resources are provisioned.                                                      | Yes          | `eastus`                                 |
| `prefix`                                      | A string prefix applied to naming deployed resources.                                              | No           | `bluerock-premium`                       |
| `allow_ip`                                    | Ingress CIDR IP range permitted to access the network.                                             | Yes          | `0.0.0.0/0`                              |
| `vnet_cidr`                                   | CIDR block defined for the Virtual Network.                                                        | Yes          | `10.20.0.0/16`                           |
| `subnet_cidr`                                 | CIDR block defined for the subnet.                                                                 | Yes          | `10.20.1.0/24`                           |
| `existing_vnet_name`                          | Optional: Name of a pre-existing Virtual Network to use instead of creating a new one.             | No           | `vnet-shared-prod`                       |
| `existing_subnet_name`                        | Optional: Name of a pre-existing subnet to use.                                                    | No           | `snet-shared-prod`                       |
| `existing_network_resource_group_name`        | Optional: Resource group name of the existing network.                                             | No           | `rg-network-shared`                      |
| `vm_size`                                     | Hardware profile/size for the virtual machine.                                                     | Yes          | `Standard_D4s_v5`                        |
| `os_disk_type`                                | Storage tier for the operating system disk.                                                        | Yes          | `Premium_LRS`                            |
| `admin_username`                              | Primary administrator username for the instance.                                                   | Yes          | `ubuntu`                                 |
| `existing_ssh_public_key_name`                | Name of an existing SSH public key resource.                                                       | Yes          | `my-ssh-key`                             |
| `existing_ssh_public_key_resource_group_name` | Resource group containing the existing SSH key.                                                    | Yes          | `shared-keys-rg`                         |
| `boot_image`                                  | Required: Azure custom image resource ID.                                                          | Yes          | `/subscriptions/.../images/<image-name>` |
| `policy_storage_account_name`                 | Globally unique storage account name for policy artifacts. Must be 3-24 lowercase letters/numbers. | Yes          | `bluerockpremiumpolicy01`                |
| `existing_storage_account_name`               | Optional: Name of a pre-existing storage account for policy files.                                 | No           | `stsharedpolicy01`                       |
| `existing_storage_container_name`             | Optional: Name of a pre-existing storage container for policy files.                               | No           | `policies`                               |
| `existing_storage_resource_group_name`        | Optional: Resource group of the existing storage account.                                          | No           | `rg-storage-shared`                      |
| `sample_host_name`                            | Hostname assigned to the runtime node.                                                             | Yes          | `bluerock-premium-node-01`               |
| `enable_external_otel`                        | Flag to enable external OpenTelemetry integration.                                                 | Yes          | `true`                                   |
| `monitor_resource_group_name`                 | Optional: Resource group name for monitoring integration.                                          | No           | `rg-bluerock-sentinel`                   |
| `create_monitor_resource_group`               | Optional: Flag indicating whether to create a new monitor resource group.                          | No           | `false`                                  |
| `log_analytics_workspace_name`                | Name of the Log Analytics Workspace.                                                               | Yes          | `law-bluerock`                           |
| `log_analytics_retention_days`                | Data retention period for Log Analytics in days.                                                   | Yes          | `90`                                     |
| `app_insights_name`                           | Name of the Application Insights component.                                                        | Yes          | `ai-bluerock`                            |
| `existing_log_analytics_workspace_name`       | Optional: Name of an existing Log Analytics Workspace to reuse.                                    | No           | `law-shared`                             |
| `existing_app_insights_name`                  | Optional: Name of an existing Application Insights component to reuse.                             | No           | `ai-shared`                              |

{% hint style="info" icon="notes-sticky" %}
**Note:**&#x20;

Ensure the specified `vm_size` (e.g., `Standard_D2s_v3`) is available in the target `location` and meets Azure quota limits. Since Azure enforces VM deployments based on regional vCPU counts, verify sufficient capacity using:

```shellscript
$ az vm list-usage --location eastus --query "[?contains(localName, 'vCPU')].{Name:localName, Current:currentValue, Limit:limit}" -o table
```

{% endhint %}

***

### Configuration Steps

To prepare the environment for deployment, follow these steps to initialize the variables:

1. Navigate to the deployment directory:

   ```shellscript
   $ cd azure/compute/terraform/ubuntu/PublicDeployment/
   ```
2. Copy the example variables file to create a live configuration file:

   ```shellscript
   $ cp terraform.tfvars.example terraform.tfvars
   ```
3. Edit the `terraform.tfvars` file and populate the mandatory fields, including the `subscription_id`, `boot_image`, and networking variables.

{% hint style="info" icon="notes-sticky" %}
**Note:**

If deploying into an existing network or storage account, ensure the respective `existing_*` variables (e.g., `existing_vnet_name`, `existing_subnet_name`, `existing_storage_account_name`) are explicitly defined. Otherwise, Terraform will provision entirely new network and storage resources .
{% endhint %}

***

### Running the Deployment

Execute the standard Terraform workflow to provision the Azure infrastructure:

1. Initialize the working directory:

   ```shellscript
   $ terraform init
   ```
2. Review the execution plan:

   ```shellscript
   $ terraform plan
   ```
3. Apply the configuration:

   ```shellscript
   $ terraform apply
   ```

***

### Post-Deployment Validation

1. Check BlueRock Instance:\
   Verify the VM status and retrieve the Public IP address using the Azure CLI:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ az vm list -d --query "[?name=='&#x3C;prefix>-node'].{Name:name, Status:powerState, PublicIP:publicIps}" -o table
   </code></pre>

   For example:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ az vm list -d --query "[?name=='bluerock-test-node'].{Name:name, Status:powerState, PublicIP:publicIps}" -o table
   </code></pre>

   Expected output:

   <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">Name                Status      PublicIP
   ------------------  ----------  ----------
   bluerock-test-node  VM running  &#x3C;public-ip-address>
   </code></pre>
2. Verify Services:\
   Establish an SSH connection to the instance and verify the BlueRock control plane status:
   1. Connect to the instance:

      <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ ssh -i &#x3C;path-to-priv-key> ubuntu@&#x3C;ssh-ip-address>
      </code></pre>
   2. Check the service status

      <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ sudo systemctl status uc-docker.service
      </code></pre>

      Expected Output:

      <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">● uc-docker.service - Manage the Ultracontrol Docker Service
           Loaded: loaded (/etc/systemd/system/uc-docker.service; enabled; preset: enabled)
           Active: active (running) since Tue 2026-04-28 15:31:22 UTC; 12h ago
         Main PID: 2430 (uc-docker.sh)
            Tasks: 2 (limit: 4614)
           Memory: 648.0K (peak: 9.0M)
              CPU: 18.644s
           CGroup: /system.slice/uc-docker.service
                   ├─ 2430 /usr/bin/bash /opt/bluerock/bin/uc-docker.sh start
                   └─21732 sleep 5

      Apr 28 15:31:22 bluerock-test-node systemd[1]: uc-docker.service: Scheduled restart job, restart counter is at 1.
      Apr 28 15:31:22 bluerock-test-node systemd[1]: Started uc-docker.service - Manage the Ultracontrol Docker Service.
      </code></pre>
   3. Verify running containers

      ```shellscript
      $ sudo docker ps
      ```

      Expected output:

      ```shellscript
      CONTAINER ID   IMAGE                                         COMMAND                  CREATED        STATUS        PORTS                                                                              NAMES
      27c41fd6d422   ultracontrol:latest                           "/opt/bluerock/sbin/…"   13 hours ago   Up 13 hours                                                                                      uc
      3f7fd76c0526   otel/opentelemetry-collector-contrib:0.95.0   "/otelcol-contrib --…"   13 hours ago   Up 13 hours   0.0.0.0:4317-4318->4317-4318/tcp, [::]:4317-4318->4317-4318/tcp, 55678-55679/tcp   otel-collector
      ```
   4. Verify telemetry flow (optional)

      <pre class="language-shellscript" data-overflow="wrap"><code class="lang-shellscript">$ sudo docker logs otel-collector
      </code></pre>

      Expected output:

      ```shellscript
      2026-04-28T15:31:21.786Z	info	service@v0.95.0/telemetry.go:55	Setting up own telemetry...
      2026-04-28T15:31:21.787Z	info	service@v0.95.0/telemetry.go:97	Serving metrics	{"address": ":8888", "level": "Basic"}
      2026-04-28T15:31:21.791Z	info	service@v0.95.0/service.go:143	Starting otelcol-contrib...	{"Version": "0.95.0", "NumCPU": 2}
      2026-04-28T15:31:21.791Z	info	extensions/extensions.go:34	Starting extensions...
      ```

***

### Cleanup of Deployed Resource

To remove or delete the deployed BlueRock instances or all resources, run the following command.

```shellscript
$ terraform destroy --auto-approve
```

{% hint style="info" icon="notes-sticky" %}
**Note:**

Azure Monitor generates hidden Smart Detector Alert Rules that block the `terraform destroy` command. Clear these rules beforehand by executing:

```shellscript
$ az resource list -g "<MONITOR_RG>" --resource-type "microsoft.alertsmanagement/smartDetectorAlertRules" --query "[].id" -o tsv | xargs -I {} az resource delete --ids "{}"
```

{% endhint %}

***

### View Logs in Azure

For detailed instructions on how to view logs and OTEL events from the instance, please refer to the [View Logs in Azure](/deployment-guides/azure-deployments/azure-deployment-with-cli.md#view-logs-in-azure) section.

***

### Configuring Remote Project Workspace in Claude Desktop IDE

For detailed instructions on how to set up and connect your remote environment, refer to the [Configuring Claude Desktop IDE](/deployment-guides/azure-deployments/azure-deployment-with-cli.md#configuring-remote-project-workspace-in-claude-desktop-ide) section.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bluerock.io/deployment-guides/azure-deployments/azure-deployment-with-terraform.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
