# Authoring desired state

This guide explains how to **author the desired state** in MechCloud Stateless IaC for Azure. You define your infrastructure using a clean, flat YAML format that is inspired by ARM templates but significantly simpler — no complex nested JSON, no `resourceId()` functions, no deployment state files, and no `camelCase`. Instead, you use **`snake_case` for properties**, **`ref:` syntax** for cross-resource references, and **`defaults:`** to avoid repetition.

### Key Principles

| Principle                   | Description                                                                                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Flat Resource List**      | All resources are declared in a single flat `resources:` list. Parent-child relationships are expressed via `ref:` syntax.                 |
| **`snake_case` Properties** | All keys use lowercase with underscores (e.g., `address_prefixes`, not `addressPrefixes`).                                                 |
| **Defaults Block**          | Use `defaults:` to specify common values (like `resource_group`) once, avoiding repetition across all resources.                           |
| **`ref:` for IDs**          | Use `ref:<name>` to reference another resource's ID. MechCloud resolves this to the full Azure resource ID at plan/apply time.             |
| **Nested Path References**  | Use `ref:<parent>/<child_type>/<child_name>` to reference nested resources (e.g., `ref:vnet1/subnets/subnet1` for a subnet inside a VNet). |
| **API Version Required**    | Each resource must specify its `api_version` to ensure compatibility with Azure Resource Manager.                                          |

### Step-by-Step: From ARM Template to MechCloud

Let's convert a typical ARM template that creates a VM with networking into a MechCloud desired state.

#### ARM Template (Original)

```json
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    {
      "type": "Microsoft.Network/virtualNetworks",
      "apiVersion": "2025-05-01",
      "name": "vnet1",
      "location": "[resourceGroup().location]",
      "properties": {
        "addressSpace": {
          "addressPrefixes": ["10.0.0.0/16"]
        },
        "subnets": [
          {
            "name": "subnet1",
            "properties": {
              "addressPrefixes": ["10.0.1.0/24"]
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2025-05-01",
      "name": "nsg1",
      "location": "[resourceGroup().location]",
      "properties": {
        "securityRules": [
          {
            "name": "allow-ssh",
            "properties": {
              "priority": 100,
              "direction": "Inbound",
              "access": "Allow",
              "protocol": "Tcp",
              "sourcePortRange": "*",
              "destinationPortRange": "22",
              "sourceAddressPrefix": "YOUR_IP/32",
              "destinationAddressPrefix": "*"
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Network/networkInterfaces",
      "apiVersion": "2025-05-01",
      "name": "nic1",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/virtualNetworks', 'vnet1')]",
        "[resourceId('Microsoft.Network/networkSecurityGroups', 'nsg1')]"
      ],
      "properties": {
        "networkSecurityGroup": {
          "id": "[resourceId('Microsoft.Network/networkSecurityGroups', 'nsg1')]"
        },
        "ipConfigurations": [
          {
            "name": "ipconfig1",
            "properties": {
              "subnet": {
                "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet1', 'subnet1')]"
              },
              "privateIPAllocationMethod": "Dynamic"
            }
          }
        ]
      }
    },
    {
      "type": "Microsoft.Compute/virtualMachines",
      "apiVersion": "2025-04-01",
      "name": "vm1",
      "location": "[resourceGroup().location]",
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkInterfaces', 'nic1')]"
      ],
      "properties": {
        "hardwareProfile": {
          "vmSize": "Standard_B2pts_v2"
        },
        "osProfile": {
          "computerName": "testvm",
          "adminUsername": "ubuntu",
          "adminPassword": "SecureP@ss2024!"
        },
        "networkProfile": {
          "networkInterfaces": [
            {
              "id": "[resourceId('Microsoft.Network/networkInterfaces', 'nic1')]"
            }
          ]
        },
        "storageProfile": {
          "imageReference": {
            "publisher": "Canonical",
            "offer": "ubuntu-24_04-lts",
            "sku": "server-arm64",
            "version": "latest"
          },
          "osDisk": {
            "createOption": "FromImage",
            "managedDisk": {
              "storageAccountType": "StandardSSD_LRS"
            }
          }
        }
      }
    }
  ]
}
```

#### MechCloud Desired State (Converted)

```yaml
defaults:
  resource_group: rg1

resources:
  - type: "Microsoft.Network/virtualNetworks"
    api_version: "2025-05-01"
    name: vnet1
    props:
      address_space:
        address_prefixes:
          - "10.0.0.0/16"
      subnets:
        - name: subnet1
          props:
            address_prefixes:
              - "10.0.1.0/24"

  - type: "Microsoft.Network/networkSecurityGroups"
    api_version: "2025-05-01"
    name: nsg1
    props:
      security_rules:
        - name: allow-ssh
          props:
            priority: 100
            direction: Inbound
            access: Allow
            protocol: Tcp
            source_port_range: "*"
            destination_port_range: "22"
            source_address_prefix: "{{CURRENT_IP}}/32"
            destination_address_prefix: "*"

  - type: "Microsoft.Network/networkInterfaces"
    api_version: "2025-05-01"
    name: nic1
    props:
      network_security_group:
        id: "ref:nsg1"
      ip_configurations:
        - name: ipconfig1
          props:
            subnet:
              id: "ref:vnet1/subnets/subnet1"
            private_ip_allocation_method: Dynamic

  - type: "Microsoft.Compute/virtualMachines"
    api_version: "2025-04-01"
    name: vm1
    props:
      hardware_profile:
        vm_size: Standard_B2pts_v2
      os_profile:
        computer_name: testvm
        admin_username: ubuntu
        admin_password: SecureP@ss2024!
      network_profile:
        network_interfaces:
          - id: "ref:nic1"
      storage_profile:
        image_reference:
          publisher: Canonical
          offer: ubuntu-24_04-lts
          sku: server-arm64
          version: latest
        os_disk:
          create_option: FromImage
          managed_disk:
            storage_account_type: StandardSSD_LRS
```

### Key Simplifications

#### 1. The `defaults:` Block — Eliminate Repetition

In ARM templates, you must specify `location` and often reference the resource group repeatedly using functions like `[resourceGroup().location]`. In MechCloud, the `defaults:` block lets you define common values once:

```yaml
defaults:
  resource_group: rg1
```

This value is automatically applied to all resources in the template. You don't need to repeat `resource_group: rg1` for each resource.

**Benefits:**

* **Less repetition** — Define once, apply everywhere
* **Fewer errors** — No risk of typos or inconsistent values
* **Cleaner templates** — Focus on what makes each resource unique

#### 2. The `ref:` Syntax — Simple Resource References

ARM templates require verbose `resourceId()` function calls to reference other resources:

```json
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', 'nsg1')]"
```

In MechCloud, you simply use:

```yaml
id: "ref:nsg1"
```

MechCloud automatically resolves `ref:nsg1` to the full Azure resource ID at plan/apply time:

```
/subscriptions/{subscription-id}/resourceGroups/rg1/providers/Microsoft.Network/networkSecurityGroups/nsg1
```

#### 3. Nested Resource References — Clean Path Syntax

For nested resources like subnets (which are children of VNets), ARM templates require complex expressions:

```json
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet1', 'subnet1')]"
```

In MechCloud, you use a simple path notation:

```yaml
id: "ref:vnet1/subnets/subnet1"
```

The path follows the pattern: `<parent_name>/<child_type>/<child_name>`

MechCloud resolves this to:

```
/subscriptions/{subscription-id}/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/subnet1
```

#### 4. No `dependsOn` Required

ARM templates require explicit `dependsOn` arrays to ensure resources are created in the correct order:

```json
"dependsOn": [
  "[resourceId('Microsoft.Network/virtualNetworks', 'vnet1')]",
  "[resourceId('Microsoft.Network/networkSecurityGroups', 'nsg1')]"
]
```

In MechCloud, dependencies are **automatically inferred** from `ref:` references. When you write:

```yaml
network_security_group:
  id: "ref:nsg1"
```

MechCloud knows that `nic1` depends on `nsg1` and will create them in the correct order. No manual dependency management required.

### Conversion Rules

| ARM Template                                        | → | MechCloud                                             |
| --------------------------------------------------- | - | ----------------------------------------------------- |
| `"type": "Microsoft.Network/virtualNetworks"`       | → | `type: "Microsoft.Network/virtualNetworks"`           |
| `"apiVersion": "2025-05-01"`                        | → | `api_version: "2025-05-01"`                           |
| `"name": "vnet1"`                                   | → | `name: vnet1`                                         |
| `"properties": { ... }`                             | → | `props: ...`                                          |
| `addressPrefixes`                                   | → | `address_prefixes`                                    |
| `privateIPAllocationMethod`                         | → | `private_ip_allocation_method`                        |
| `storageAccountType`                                | → | `storage_account_type`                                |
| `"[resourceGroup().location]"`                      | → | **Remove** — derived from selected Region in UI       |
| `"[resourceId('...', 'name')]"`                     | → | `"ref:name"`                                          |
| `"[resourceId('Type/SubType', 'parent', 'child')]"` | → | `"ref:parent/subtype/child"`                          |
| `"dependsOn": [...]`                                | → | **Remove** — automatically inferred from `ref:` usage |

### Template Variables

MechCloud supports special template variables that are resolved at plan/apply time:

| Variable         | Description                                                                                                     |
| ---------------- | --------------------------------------------------------------------------------------------------------------- |
| `{{CURRENT_IP}}` | Replaced with the public IP address of the machine executing the plan. Useful for firewall rules and NSG rules. |

**Example usage in NSG rule:**

```yaml
source_address_prefix: "{{CURRENT_IP}}/32"
```

This eliminates the need to manually look up and update your IP address every time it changes.

### Reference Syntax Summary

| Scenario                       | ARM Template                                                                    | MechCloud                   |
| ------------------------------ | ------------------------------------------------------------------------------- | --------------------------- |
| Reference a top-level resource | `[resourceId('Microsoft.Network/networkSecurityGroups', 'nsg1')]`               | `ref:nsg1`                  |
| Reference a nested resource    | `[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet1', 'subnet1')]` | `ref:vnet1/subnets/subnet1` |
| Reference a NIC from a VM      | `[resourceId('Microsoft.Network/networkInterfaces', 'nic1')]`                   | `ref:nic1`                  |

### Resource Paths

Each resource in MechCloud has a unique path based on its name and type. For nested resources, the path includes the parent:

| Resource                | Path                    |
| ----------------------- | ----------------------- |
| Virtual Network         | `vnet1`                 |
| Subnet (nested in VNet) | `vnet1/subnets/subnet1` |
| Network Security Group  | `nsg1`                  |
| Network Interface       | `nic1`                  |
| Virtual Machine         | `vm1`                   |

### Referencing Existing Resources

When you need to reference resources that already exist in Azure (not defined in your template), MechCloud supports shorthand formats that are automatically expanded to full Azure resource IDs.

#### Same Resource Group

For existing resources in the **same resource group** as your template's default, use the shorthand format starting with `/providers`:

```yaml
subnet:
  id: "/providers/Microsoft.Network/virtualNetworks/existing-vnet/subnets/existing-subnet"
```

MechCloud automatically expands this to the full Azure resource ID:

```
/subscriptions/{subscription-id}/resourceGroups/{resource-group}/providers/Microsoft.Network/virtualNetworks/existing-vnet/subnets/existing-subnet
```

#### Different Resource Group (Same Subscription)

For existing resources in a **different resource group** (but same subscription), use the format starting with `/resourceGroups`:

```yaml
subnet:
  id: "/resourceGroups/network-rg/providers/Microsoft.Network/virtualNetworks/shared-vnet/subnets/app-subnet"
```

MechCloud automatically expands this to the full Azure resource ID:

```
/subscriptions/{subscription-id}/resourceGroups/network-rg/providers/Microsoft.Network/virtualNetworks/shared-vnet/subnets/app-subnet
```

**Note**: All resources are provisioned in the same subscription (from the selected cloud account). The subscription ID is automatically obtained from the cloud account metadata — you never need to specify it.

#### Reference Format Summary

| Scenario                                      | Format                                                        |
| --------------------------------------------- | ------------------------------------------------------------- |
| Resource in same template                     | `ref:resource-name` or `ref:parent/child-type/child-name`     |
| Existing resource in same resource group      | `/providers/Microsoft.{RP}/{type}/{name}`                     |
| Existing resource in different resource group | `/resourceGroups/{rg}/providers/Microsoft.{RP}/{type}/{name}` |

#### Example: VM Using Existing VNet

This example creates a VM that uses an existing VNet and subnet:

```yaml
defaults:
  resource_group: app-rg

resources:
  - type: "Microsoft.Network/networkInterfaces"
    api_version: "2025-05-01"
    name: nic1
    props:
      ip_configurations:
        - name: ipconfig1
          props:
            subnet:
              id: "/providers/Microsoft.Network/virtualNetworks/existing-vnet/subnets/app-subnet"

  - type: "Microsoft.Compute/virtualMachines"
    api_version: "2025-04-01"
    name: vm1
    props:
      hardware_profile:
        vm_size: Standard_B2pts_v2
      os_profile:
        computer_name: testvm
        admin_username: ubuntu
        admin_password: SecureP@ss2024!
      network_profile:
        network_interfaces:
          - id: "ref:nic1"
      storage_profile:
        image_reference:
          publisher: Canonical
          offer: ubuntu-24_04-lts
          sku: server-arm64
          version: latest
        os_disk:
          create_option: FromImage
          managed_disk:
            storage_account_type: StandardSSD_LRS
```

### Next Steps

1. **Select Execution Context**\
   At the top of the MechCloud console, select the execution context for your operation:
   * **Team** — e.g., `Academy (User 2)`
   * **Cloud Provider** — e.g., `Azure`
   * **Cloud Account** — e.g., `Academy`
   * **Region** — e.g., `West US 2`
   * **Context** — e.g., `azure-random1`
2. **Generate the Plan**\
   Click **Plan** to generate an execution plan.\
   MechCloud compares the desired state (defined in YAML) with the actual Azure infrastructure and determines which resources require changes.
3. **Review Plan Actions**\
   Each resource in the plan shows an `action` indicating how MechCloud will reconcile it:
   * `create` — the resource does not exist and will be created.
   * `update` — the resource exists but properties differ and will be updated.
   * `delete` — the resource exists but is no longer in the desired state and will be removed.
   * `recreate` — the resource will be deleted and then created again (for non-updatable property changes).
   * `none` — the resource is already in the desired state; no action required.
4. **Apply the Plan (When Changes Exist)**
   * The **Apply** button becomes visible **only when** the plan includes at least one resource whose `action` is not `none`.
   * Click **Apply** to provision or update resources as per the plan.
   * MechCloud will execute the required operations and bring your Azure environment to match the desired state.
5. **Verify Post-Apply State**
   * Once the apply completes, click **Plan** again to re-run the comparison.
   * If all resources now display `action: none`, the desired and actual states are in sync.
   * In this case, the **Apply** button will not be visible because no further changes are required.
6. **Handle Errors (If Any)**
   * If any errors occur during the apply step, re-run **Plan** to refresh dependencies and reconcile state.
   * Then click **Apply** again to retry provisioning with the updated plan.


---

# 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.mechcloud.io/cloud-computing/stateless-iac/azure/authoring-desired-state.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.
