> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genesiscomputing.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set Up Azure Sandboxes

> Prepare your Azure subscription, connect Genesis to it, allow network access, and confirm an agent can run code in Azure Container Instances.

Setting up Azure sandboxes takes four parts, usually by two people:

1. An **Azure administrator** prepares the subscription: registers Container Instances, creates
   a resource group for sandboxes, and creates a service principal that can act only in it.
2. A **Genesis administrator** saves those details as an Azure connection in Genesis and makes it
   the default for the people who will use it.
3. Whoever runs Genesis **allows its outbound traffic** to Azure. In the Snowflake Native App, this
   is an approval in Snowflake.
4. Anyone **checks that it works** by asking a Genesis agent.

Plan on about 30 minutes in total. This page explains each step so you know what you are creating
and why. For background, see [Azure Sandboxes](/docs/Setup/Azure-Sandboxes-Overview).

## Before you start

### Who you need

| Who | What they need |
| - | - |
| **Azure administrator** | In the Azure subscription: permission to register resource providers and create resource groups (for example **Contributor** or **Owner** on the subscription), and permission to assign roles on the new resource group (for example **Owner** or **User Access Administrator**). In Microsoft Entra ID: permission to create app registrations. For the least-privilege option, also permission to create custom roles. |
| **Genesis administrator** | Permission to create connections in Genesis. To set a default for a role, also `manage_all_connections`. |
| **Snowflake administrator** (Snowflake Native App only) | A role that can approve the Genesis app's external access requests, for example `ACCOUNTADMIN`. |

### Values to collect

Write these down as you go. You enter all of them in Genesis at the end.

| Value | Example | Where it comes from |
| - | - | - |
| **Azure AD Tenant ID** | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | Your Microsoft Entra tenant. Shown as `tenant` when you create the service principal. |
| **Client ID** | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | The service principal's application ID (`appId`). |
| **Client secret** | (shown once) | Created with the service principal (`password`). |
| **Subscription ID** | `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | The subscription the sandboxes will run and be billed in. |
| **Resource group** | `genesis-sandboxes` | The resource group you create below. |
| **Region** | `eastus2` | The Azure region the sandboxes will run in. Either form Azure shows works: `eastus2` or `East US 2`. Genesis uses the short form, such as `eastus2` or `westeurope`. |

<Tip>
  Choose a region close to the data the agents will work with. If your Snowflake account runs on
  Azure, use the same region as your Snowflake account.
</Tip>

## Prepare Azure

The examples use the resource group name `genesis-sandboxes` and the region `eastus2`. Replace
`<SUBSCRIPTION_ID>` with your subscription ID throughout.

<Steps>
  <Step title="Sign in and choose the subscription">
    Every later step acts on one subscription, so pick it first.

    <Tabs>
      <Tab title="Azure CLI">
        ```bash theme={null}
        az login
        az account set --subscription <SUBSCRIPTION_ID>
        ```
      </Tab>

      <Tab title="Azure portal">
        Sign in at [portal.azure.com](https://portal.azure.com). Open **Subscriptions**, select the
        subscription the sandboxes will run in, and copy its **Subscription ID**.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Register Azure Container Instances">
    A subscription can create a type of Azure resource only after its resource provider is
    registered. New subscriptions often do not have Container Instances registered yet, and
    without it every sandbox start fails. Registering is free and safe to repeat.

    <Tabs>
      <Tab title="Azure CLI">
        ```bash theme={null}
        az provider register --namespace Microsoft.ContainerInstance
        az provider show -n Microsoft.ContainerInstance --query registrationState -o tsv
        ```

        Registration can take a few minutes. Repeat the second command until it prints
        `Registered`.
      </Tab>

      <Tab title="Azure portal">
        1. Open **Subscriptions**, then your subscription.
        2. Under **Settings**, select **Resource providers**.
        3. Search for `Microsoft.ContainerInstance`, select it, and click **Register**.
        4. Wait until its status shows **Registered**.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Create a resource group for sandboxes">
    The resource group is the boundary of what Genesis can touch. Use a new, dedicated resource
    group that holds nothing else, so that the permission you grant Genesis in the next step
    reaches nothing else. It also lets you see sandbox costs on their own.

    <Tabs>
      <Tab title="Azure CLI">
        ```bash theme={null}
        az group create --name genesis-sandboxes --location eastus2
        ```
      </Tab>

      <Tab title="Azure portal">
        1. Open **Resource groups** and click **Create**.
        2. Choose the subscription, enter `genesis-sandboxes` as the name, and choose the region.
        3. Click **Review + create**, then **Create**.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Create the service principal Genesis signs in as">
    Genesis needs an identity of its own in Azure: a service principal (an app registration in
    Microsoft Entra ID) with a client secret. Give it **Contributor** on the sandbox resource group
    only. Contributor there lets it create, use and delete container groups in that resource group,
    and nothing anywhere else. If your security policy does not allow Contributor, use the
    [least-privilege custom role](#least-privilege-alternative-custom-role) instead.

    <Tabs>
      <Tab title="Azure CLI">
        ```bash theme={null}
        az ad sp create-for-rbac \
          --name genesis-sandboxes \
          --role Contributor \
          --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/genesis-sandboxes
        ```

        The command prints JSON like this:

        ```json theme={null}
        {
          "appId": "11111111-2222-3333-4444-555555555555",
          "displayName": "genesis-sandboxes",
          "password": "<client secret>",
          "tenant": "66666666-7777-8888-9999-000000000000"
        }
        ```

        | Output field | Genesis field |
        | - | - |
        | `appId` | **Client ID** |
        | `password` | **Client Secret** |
        | `tenant` | **Azure AD Tenant ID** |

        The secret expires after one year by default. Add `--years <N>` to choose another
        lifetime.
      </Tab>

      <Tab title="Azure portal">
        1. Open **Microsoft Entra ID**, then **App registrations**, and click **New registration**.
        2. Enter the name `genesis-sandboxes`, leave the other settings at their defaults, and click
           **Register**.
        3. On the app's **Overview** page, copy the **Application (client) ID** (the Genesis
           **Client ID**) and the **Directory (tenant) ID** (the Genesis **Azure AD Tenant ID**).
        4. Open **Certificates & secrets**, then **Client secrets**, and click **New client
           secret**. Choose an expiry and click **Add**.
        5. Copy the secret's **Value** now (the Genesis **Client Secret**). Copy the **Value**, not
           the **Secret ID**.
        6. Open **Resource groups**, then `genesis-sandboxes`, then **Access control (IAM)**.
        7. Click **Add**, then **Add role assignment**. Select the **Contributor** role. In the
           current portal it is listed under **Privileged administrator roles**.
        8. On **Members**, choose **User, group, or service principal**, click **Select members**,
           search for `genesis-sandboxes`, and select it.
        9. Click **Review + assign**.
      </Tab>
    </Tabs>

    <Warning>
      Azure shows the client secret only once. Copy it straight into Genesis or your password
      manager. Never paste it into a chat, a ticket or a shared document.
    </Warning>

    <Note>
      A new role assignment can take a minute or two to take effect. If the check in
      [Verify it works](#verify-it-works) reports a missing permission right after you assign the
      role, wait a moment and try again.
    </Note>
  </Step>
</Steps>

### Least-privilege alternative: custom role

Contributor on a dedicated resource group is the simplest choice. If you want the service
principal to hold only what Genesis actually uses, create this custom role and assign it instead
of Contributor. It allows reading the resource group and reading, creating, deleting and running
commands in container groups, and nothing else. A service principal with only this role has been
tested to pass the Genesis permission check and to start, use and delete sandboxes.

Save the role as `genesis-sandbox-role.json`, replacing `<SUBSCRIPTION_ID>`:

```json theme={null}
{
  "Name": "Genesis Sandbox Runner",
  "Description": "Minimal permissions for Genesis Azure Container Instances sandboxes.",
  "Actions": [
    "Microsoft.ContainerInstance/containerGroups/read",
    "Microsoft.ContainerInstance/containerGroups/write",
    "Microsoft.ContainerInstance/containerGroups/delete",
    "Microsoft.ContainerInstance/containerGroups/containers/exec/action",
    "Microsoft.Resources/subscriptions/resourceGroups/read"
  ],
  "NotActions": [],
  "AssignableScopes": ["/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/genesis-sandboxes"]
}
```

| Permission | Why Genesis needs it |
| - | - |
| `containerGroups/read` | Check that a sandbox has started, and find it again after a Genesis restart. |
| `containerGroups/write` | Create the sandbox's container group. |
| `containerGroups/delete` | Delete the container group when the sandbox stops or times out. |
| `containerGroups/containers/exec/action` | Run each command in the container. |
| `resourceGroups/read` | Confirm the resource group exists and is reachable, for **Test Connection** and the agent's check. |

<Tabs>
  <Tab title="Azure CLI">
    Create the role, create the service principal without a role, then assign the custom role on
    the resource group:

    ```bash theme={null}
    az role definition create --role-definition @genesis-sandbox-role.json

    az ad sp create-for-rbac --name genesis-sandboxes --skip-assignment

    az role assignment create \
      --assignee <appId from the previous output> \
      --role "Genesis Sandbox Runner" \
      --scope /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/genesis-sandboxes
    ```

    If your version of the Azure CLI does not accept `--skip-assignment`, run
    `az ad sp create-for-rbac --name genesis-sandboxes` without it and, if it assigned a role,
    remove that assignment with `az role assignment delete`.
  </Tab>

  <Tab title="Azure portal">
    1. Open **Resource groups**, then `genesis-sandboxes`, then **Access control (IAM)**.
    2. Click **Add**, then **Add custom role**.
    3. On the **JSON** tab, click **Edit**, paste the role above, click **Save**, then
       **Review + create** and **Create**.
    4. Create the app registration and client secret as in steps 1 to 5 of the portal tab above.
    5. Assign the role as in steps 6 to 9, choosing **Genesis Sandbox Runner** instead of
       **Contributor**. Custom roles are listed under **Job function roles**.
  </Tab>
</Tabs>

<Note>
  If you plan to attach sandboxes to a subnet in your virtual network, Azure also requires the
  service principal to have permission to join that subnet. See
  [Run inside your virtual network](/docs/Setup/Azure-Sandboxes-Using#run-inside-your-virtual-network).
</Note>

## Create the Azure connection in Genesis

<Steps>
  <Step title="Open the Azure connection form">
    In Genesis, open **Config -> Connections**, choose **Cloud & Storage**, then **Azure**. The
    authentication mode is **Service Principal**.
  </Step>

  <Step title="Enter the values you collected">
    | Field | Required | Value |
    | - | - | - |
    | **Azure AD Tenant ID** | Yes | The tenant ID. |
    | **Client ID** | Yes | The service principal's application (client) ID. |
    | **Client Secret** | Yes | The client secret. Genesis stores it as an encrypted secret and never shows it again. |
    | **Subscription ID** | Yes | The subscription the sandboxes run and are billed in. |
    | **Resource Group** | Yes | `genesis-sandboxes`, or the name you chose. |
    | **Region** | No | For example `eastus2`. If you leave it empty, Genesis uses `eastus`. |

    Set **Region** even if you want `eastus`. The region decides where sandboxes run and, in the
    Snowflake Native App, which Azure command endpoint Genesis is allowed to reach. Genesis
    accepts the programmatic name (`eastus2`) or the display name (`East US 2`), saves the
    programmatic name, and refuses to start a sandbox through this connection in any other
    region.

    To share the connection with a team, make it a **Shared connection** and, if only some
    people should use it, list their roles under **Roles with Access**. See
    [Personal vs shared connections](/docs/Setup/Connections#personal-vs-shared-connections).
  </Step>

  <Step title="Save, then click Test Connection">
    **Test Connection** gets an Azure access token for the service principal from Microsoft
    Entra ID and reads the resource group. A success means the tenant ID, client ID, secret,
    subscription and resource group are all correct.

    Test Connection does not check the container permissions. Run the agent check in
    [Verify it works](#verify-it-works) for that.

    In the Snowflake Native App, approve external access first; see
    [Snowflake Native App](#snowflake-native-app). Until it is approved, Test Connection cannot
    reach Azure.
  </Step>

  <Step title="Make it the default for the people who use it">
    Agents look for the Azure connection to use in this order: a connection named in the request,
    then the user's own default (**My default**), then a default set for one of the user's roles
    (**Role default**). There is no workspace-wide default, so without one of these an agent
    cannot find the connection unless it is named.

    * **Just you:** turn on **Make this my default connection** in the connection form.
    * **A team:** on the shared connection's row, set it as the **Role default** for the team's
      role. This needs `manage_all_connections`.

    See [Default connection layers](/docs/Setup/Connections#default-connection-layers).
  </Step>
</Steps>

## Allow Genesis to reach Azure

Genesis itself, not the sandbox, connects to three Azure destinations. All three are outbound
from Genesis. Nothing connects in.

| Destination | Port | Used for |
| - | - | - |
| `login.microsoftonline.com` | 443 | Getting access tokens from Microsoft Entra ID. |
| `management.azure.com` | 443 | Creating, checking and deleting container groups through Azure Resource Manager. |
| `*.<region>.atlas.cloudapp.azure.com`, for example `*.eastus2.atlas.cloudapp.azure.com` | **19390** | Running commands. Azure gives each command session its own host name under this domain. |

<Warning>
  The command endpoint uses port **19390**, not 443. A firewall that allows only HTTPS on port 443
  lets sandboxes start but makes every command fail with
  `cannot connect to exec host ...:19390`.
</Warning>

How you allow these depends on where Genesis runs.

### Snowflake Native App

In the Snowflake Native App, Genesis can reach only the destinations Snowflake allows through an
external access integration that an administrator approves. The Azure connection declares the
three destinations itself, derived from its **Region**:

```text theme={null}
login.microsoftonline.com
management.azure.com
*.eastus2.atlas.cloudapp.azure.com:19390
```

The third entry is specific to the region because a Snowflake network rule wildcard covers only
one level of subdomain: `*.cloudapp.azure.com` would not match a host such as
`abc123.eastus2.atlas.cloudapp.azure.com`.

<Steps>
  <Step title="Save the connection">
    After you save the Azure connection, Genesis opens the setup guide for it at the top of the
    Connections page. The connection shows **egress pending** until the external
    access step is done, and the guide lists the three destinations above.
  </Step>

  <Step title="Click Approve in Snowflake">
    Genesis registers the three destinations with Snowflake and opens the Genesis app in
    Snowsight.
  </Step>

  <Step title="Approve the external access request in Snowsight">
    A Snowflake user with a role that can approve the app's requests, such as `ACCOUNTADMIN`,
    reviews the **Genesis External Access Integration** request, checks that it lists the three
    destinations above for your region, and approves it. Snowflake then creates the network rule
    and external access integration from that list.
  </Step>

  <Step title="Return to Genesis and test">
    Back in Genesis, click **Check Again** in the setup guide. Genesis attaches the integration
    to its service and the connection becomes ready. Then click **Test Connection**.
  </Step>
</Steps>

Changes to the network rule take effect within about 20 seconds, without restarting Genesis.
We tested this on a Snowflake Native App with the SQL equivalent of the Snowsight approval (the
endpoint registration, a network rule and external access integration holding the three entries,
and attaching it to Genesis), not by clicking through Snowsight itself. Snowflake accepted the
region-specific wildcard with its `:19390` port. The SQL path below is also the fallback if the
Snowsight approval does not complete.
If you later change the connection's **Region**, its command endpoint changes too, and the new
destination must be approved the same way.

<Warning>
  Genesis can show the connection as ready, and an agent's connection check can pass, while
  commands still fail: those checks use the two port-443 destinations, and Genesis cannot read
  the contents of your network rule. The first command an agent runs in a sandbox is the real test
  of the `*.<region>.atlas.cloudapp.azure.com:19390` entry. If it fails with
  `cannot connect to exec host ...:19390: [Errno -2] Name or service not known`, that entry is
  missing or names a different region. Snowflake reports a blocked destination as a name lookup
  failure.
</Warning>

#### If your Snowflake administrator manages network rules in SQL

Some teams create network rules and integrations by hand instead of approving the request in
Snowsight. In that case, add the three destinations to the network rule of the integration bound
to the Genesis app's **Genesis External Access Integration** reference, keeping the entries it
already has. Run these as `ACCOUNTADMIN`, replacing the placeholders:

```sql theme={null}
-- Find the integration Genesis uses for its external access, then its network rule.
SHOW REFERENCES IN APPLICATION <genesis_app>;                -- row GENESIS_EXTERNAL_ACCESS
DESCRIBE INTEGRATION <genesis_external_access_integration>;  -- ALLOWED_NETWORK_RULES
DESCRIBE NETWORK RULE <network_rule>;                        -- current VALUE_LIST

-- SET replaces the whole list: repeat every existing entry, then add the three for Azure.
ALTER NETWORK RULE <network_rule> SET VALUE_LIST = (
  '<existing entries ...>',
  'login.microsoftonline.com',
  'management.azure.com',
  '*.eastus2.atlas.cloudapp.azure.com:19390'
);
```

The Azure entry must name your region and include `:19390`. A broader entry such as
`*.cloudapp.azure.com:19390` does not work, because the wildcard covers only one level of
subdomain.

For how the setup guide tracks external access, see
[Guided post-save setup](/docs/Setup/Connections#guided-post-save-setup).

### Docker, Kubernetes and virtual machines

When Genesis runs in Docker, Kubernetes or on a virtual machine, it uses your network's normal
outbound access. If outbound traffic from Genesis is unrestricted, there is nothing to do.

If an outbound firewall, network security group, egress policy or proxy controls it, allow:

| Destination | Port |
| - | - |
| `login.microsoftonline.com` | 443 |
| `management.azure.com` | 443 |
| `*.<region>.atlas.cloudapp.azure.com`, for example `*.eastus2.atlas.cloudapp.azure.com` | 19390 |

If your firewall matches wildcards across several levels of subdomain, you can allow
`*.cloudapp.azure.com` on port 19390 instead, which covers every region. Allow the region-specific
form otherwise.

These rules are about Genesis's own traffic. The sandbox container's outbound traffic, for example
`pip install`, leaves from Azure, not from your Genesis network, and is controlled in Azure; see
[Run inside your virtual network](/docs/Setup/Azure-Sandboxes-Using#run-inside-your-virtual-network).

## Verify it works

<Steps>
  <Step title="Ask an agent to check the setup">
    In a Genesis chat, ask:

    ```text theme={null}
    Check whether the Azure sandbox backend is ready to use with my Azure connection.
    ```

    The agent runs the sandbox tool's Azure check. It gets a token as the service principal, reads
    the resource group, and reads the principal's effective permissions there to confirm it can
    read, create, delete and run commands in container groups. A ready setup reports that it is
    configured, with the connection, subscription, resource group and region it will use.

    If it reports a problem, the message says what is missing; see
    [Troubleshooting](/docs/Setup/Azure-Sandboxes-Troubleshooting).
  </Step>

  <Step title="Run a first sandbox">
    Ask:

    ```text theme={null}
    Start an Azure sandbox, show me the Python version and the machine's CPU and memory,
    install pandas and print its version, then stop the sandbox.
    ```

    Expect the start to take about 30 seconds, then a few seconds for each command. While it
    runs, you can see the container group `genesis-sbx-...` in the `genesis-sandboxes` resource
    group in the Azure portal. After the agent stops the sandbox, the container group is gone.
  </Step>

  <Step title="Optional: confirm in Azure">
    ```bash theme={null}
    az container list --resource-group genesis-sandboxes -o table
    ```

    After the sandbox stops, the list is empty.
  </Step>
</Steps>

## Rotate or revoke access

* **Rotate the client secret.** Create a new secret for the service principal
  (`az ad sp credential reset --id <appId> --append`, or **Certificates & secrets** in the
  portal), enter it in the Genesis connection, save, and run **Test Connection**. Then delete the
  old secret. Put a reminder in your calendar before the secret expires: when it does, every
  sandbox start fails with a token error.
* **Revoke.** Remove the service principal's role assignment on the resource group, or delete the
  app registration. Genesis can no longer create or delete sandboxes. Delete any remaining
  `genesis-sbx-` container groups yourself.
* **Remove.** Delete the Azure connection in Genesis, then the app registration, the custom role
  if you created one, and the resource group.

## Next steps

<CardGroup cols={2}>
  <Card title="Use Azure sandboxes" icon="terminal" href="/docs/Setup/Azure-Sandboxes-Using">
    Prompts to try, images, sizes and lifecycle options.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/docs/Setup/Azure-Sandboxes-Troubleshooting">
    Every error message, what causes it, and how to fix it.
  </Card>
</CardGroup>
