Skip to content

Microsoft Azure: CLI (az)

Deploy the RTA with the az CLI. This is the fastest path: azcopy copies our published VHD server-side straight into a managed disk on Azure's backbone (minutes, not a full download/re-upload), and you never need a storage account of your own.

Before you start

Review the Microsoft Azure overview. You'll need the VHD read SAS URL from your engagement lead, the Azure CLI, azcopy, and your SSH public key.

Quick deploy

Run this recommended one-line script and it walks you through everything. First make sure you are logged in (az login), then:

curl -fsSL https://rtadocs.srt.sophos.com/platforms/azure/deploy-rta-azure.sh | bash

On Windows, use the PowerShell script: the same guided walk-through, run from a normal PowerShell window with the Azure PowerShell Az module (its page lists the one-time setup).

& ([scriptblock]::Create((irm https://rtadocs.srt.sophos.com/platforms/azure/deploy-rta-azure.ps1)))

(Already use Git Bash with the az CLI? The macOS / Linux command works there too.)

It checks you are logged in, validates your SAS first (so an expired link fails immediately, before anything else), then looks up and lets you pick your subscription, resource group, and subnet, and detects your SSH key, so you never hand-build a resource ID. It then creates the Gen2 disk, image, and VM with no public IP. Finally it waits a few minutes for the appliance's activation code in the serial log and prints it (or, if the code does not show up in time, where to read it on the console).

What you need before running

  • You are logged in: az login
  • azcopy installed
  • The VHD read SAS URL from your engagement lead (paste it when asked)
  • Your SSH public key (~/.ssh/*.pub is auto-detected)
  • An existing VNet/subnet for the appliance (you pick it from a list)
  • Any tags your Azure Policy requires on new resources (see Required tags)

Non-interactive (automation)

Pass every value as a flag and add --yes to skip the confirmation:

curl -fsSL https://rtadocs.srt.sophos.com/platforms/azure/deploy-rta-azure.sh | bash -s -- \
  --sas '<SAS_URL>' --resource-group rg-rta \
  --subnet '<FULL_SUBNET_RESOURCE_ID>' --ssh-key ~/.ssh/your_key.pub --yes
Flag Required Default
--sas yes the read SAS URL we sent you (quote it)
--resource-group yes resource group to create/use
--subnet yes full subnet resource ID (see tip below)
--ssh-key yes path to your SSH public key
--size no Standard_D2s_v5 (override, e.g. --size Standard_D4s_v5 or --size Standard_D2s_v6; an NVMe-only size such as v6 and newer first boots as Standard_D2s_v5, see NVMe sizes; Arm64, Generation 1-only, and confidential sizes are refused)
--location no auto-detected from the subnet's VNet
--vm-name / --admin-username no Sophos-RTA / azureuser
--tag KEY=VALUE no none; repeat once per tag (see Required tags)
--tags-file no none; a file with one KEY=VALUE per line

Get your full subnet resource ID

az network vnet subnet show -g <rg> --vnet-name <vnet> -n <subnet> --query id -o tsv
The script takes the full ID (starting /subscriptions/...) on purpose: it is unambiguous and works across resource groups and subscriptions.

Required tags (Azure Policy)

Some organizations use Azure Policy to deny any new resource that is missing certain tags. The deploy then fails with RequestDisallowedByPolicy and Missing Required Tags, and the error lists the tag names your policy requires. Give the script those tags and it applies them to everything it creates: the disk, image, VM, network interface, OS disk, and the resource group if it creates one. An existing resource group is used as-is and its tags are not changed.

With several tags, the easiest route is a tags file with one KEY=VALUE per line. Values can contain spaces, blank lines are ignored, and lines starting with # are comments:

cat > tags.txt <<'EOF'
Environment=Prod
Owner=Jane Doe
CostCenter=1234
EOF

curl -fsSL https://rtadocs.srt.sophos.com/platforms/azure/deploy-rta-azure.sh | bash -s -- \
  --tags-file ./tags.txt

The script still asks for everything else interactively. For a couple of tags, pass --tag once per tag instead (quote values that contain spaces). Both work alongside the other flags, including a fully non-interactive run:

curl -fsSL https://rtadocs.srt.sophos.com/platforms/azure/deploy-rta-azure.sh | bash -s -- \
  --tag Environment=Prod --tag 'Owner=Jane Doe'

If you run the script with no tag flags, it asks whether to add tags before it deploys; answer y and enter one KEY=VALUE per line.

Prefer to run it by hand, or want to see exactly what the script does? Use the step-by-step path below.

Option 2: step by step (direct server-side copy into a managed disk)

The Quick deploy script above is Option 1; Options 2 and 3 are manual alternatives, for running each step yourself or keeping a copy of the VHD. Option 2 is the direct server-side copy into a managed disk that the script automates.

Gen2 / UEFI required, Secure Boot off

The disk and the image must be created as Gen2 / UEFI (--hyper-v-generation V2, below). A Gen1 disk boots to a black screen. Do not enable Trusted Launch / Secure Boot. Leave the security type default.

RG=rg-rta                            # your resource group
LOC=eastus                           # your region
SUBNET='<FULL_SUBNET_RESOURCE_ID>'   # az network vnet subnet show -g <rg> --vnet-name <vnet> -n <subnet> --query id -o tsv
SAS='<SAS_URL>'                      # the read SAS we provided (quote it)

az group create -n "$RG" -l "$LOC"

# 1. Create an empty Gen2 "upload" disk sized to the exact VHD byte length
az disk create -g "$RG" -n rta-disk -l "$LOC" \
  --os-type Linux --hyper-v-generation V2 \
  --upload-type Upload --upload-size-bytes 42949673472 --sku Standard_LRS

# 2. Get a temporary WRITE SAS for the disk. grep pulls the URL out of the JSON,
#    which avoids the accessSAS/accessSas field-name difference between az versions.
DISK_SAS=$(az disk grant-access -g "$RG" -n rta-disk \
  --access-level Write --duration-in-seconds 86400 -o json | grep -o 'https[^"]*')

# 3. Server-side copy our VHD straight into your disk
azcopy copy "$SAS" "$DISK_SAS" --blob-type PageBlob

# 4. Revoke the disk write SAS, then mark the disk as bootable from NVMe as well
#    as SCSI (needed only for an NVMe-only size, harmless otherwise)
az disk revoke-access -g "$RG" -n rta-disk
az disk update -g "$RG" -n rta-disk --set supportedCapabilities.diskControllerTypes="SCSI, NVMe"

# 5. Create a generalized Gen2 managed image from the disk
az image create -g "$RG" -n rta-image --os-type linux --hyper-v-generation V2 \
  --source "$(az disk show -g "$RG" -n rta-disk --query id -o tsv)"

# 6. Create the VM. Azure requires an admin username; pick any value (it's only
#    for break-glass SSH). Supply YOUR public key. No public IP and no inbound
#    rule; the appliance only needs outbound.
az vm create -g "$RG" -n Sophos-RTA \
  --image "$(az image show -g "$RG" -n rta-image --query id -o tsv)" \
  --size Standard_D2s_v5 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/your_key.pub \
  --subnet "$SUBNET" \
  --storage-sku StandardSSD_LRS \
  --public-ip-address "" --nsg ""

Want an NVMe-only size (v6 and newer, or a family such as Lsv4)?

The NVMe-only sizes are listed in the overview. Azure cannot create those straight from a managed image ("Disk controller type 'NVMe' not supported for user VM image"). Create the VM as Standard_D2s_v5 exactly as above (after the step 4 az disk update), wait for az vm create to finish, then move it:

az vm deallocate -g "$RG" -n Sophos-RTA
az vm update -g "$RG" -n Sophos-RTA \
  --set hardwareProfile.vmSize=Standard_D2s_v6 storageProfile.diskControllerType=NVMe
az vm start -g "$RG" -n Sophos-RTA

Pass the full subnet ID, not --vnet-name + name

Use the full subnet resource ID with --subnet and do not also pass --vnet-name. Mixing a full --subnet ID with --vnet-name fails with incorrect usage: --subnet ID | --subnet NAME --vnet-name NAME. The full ID is unambiguous and works even when the VNet lives in a different resource group.

Required tags (Azure Policy)

If your policy requires tags, append --tags Key1=Value1 'Key2=Value two' to the az group create, az disk create, az image create, and az vm create commands above. The OS disk that az vm create makes does not inherit the VM's tags, so tag it afterwards:

az resource tag --tags Key1=Value1 'Key2=Value two' --ids \
  "$(az vm show -g "$RG" -n Sophos-RTA --query storageProfile.osDisk.managedDisk.id -o tsv)"

--upload-size-bytes must be exact

Use 42949673472 (the exact byte length of the 40 GiB VHD). An upload disk must be created at the precise source size or the copy fails.

Don't force a Standard security type

Do not pass --security-type Standard unless the Microsoft.Compute/UseStandardSecurityType feature is registered on the subscription. For a legacy managed image the default is already a non-Trusted-Launch Gen2 VM, which is what you want.

Option 3: via your own storage account

If you want to keep a copy of the VHD in your own storage account:

# Copy our VHD into your storage account (server-side)
azcopy copy "$SAS" \
  "https://<youracct>.blob.core.windows.net/<container>/rta-generic-latest.vhd?<your-write-sas>"

# Create the image from YOUR blob, then create the VM exactly as in Option 2, step 6.
az image create -g "$RG" -n rta-image --os-type linux --hyper-v-generation V2 \
  --source "https://<youracct>.blob.core.windows.net/<container>/rta-generic-latest.vhd"

az image create cannot read a SAS URI

--source must be a plain blob URL, so your storage account has to allow the platform to read it (shared-key access enabled, the default). If your account has shared key disabled, use Option 2 (the direct server-side copy into a managed disk) instead.

Verify

# The VM lands on your VNet with a private IP only. SSH from inside your network:
az vm show -g "$RG" -n Sophos-RTA -d --query privateIps -o tsv
ssh azureuser@<private-ip>   # the admin username you set above

Send us the activation code

The VHD is generic, so on first boot the appliance comes up in activation mode. You normally don't log in at all. If you used the script, it printed the code in its final output (or, if it could not read it, where to find it on the console).

Otherwise, turn on boot diagnostics and restart the VM once. Turned on after the VM has booted, the serial log only captures what the appliance writes from then on; after the restart it prints its code again right away (the same code):

az vm boot-diagnostics enable -g "$RG" -n Sophos-RTA
az vm restart -g "$RG" -n Sophos-RTA

About a minute later, read the code from the serial log:

curl -s "$(az vm boot-diagnostics get-boot-log-uris -g "$RG" -n Sophos-RTA \
  --query serialConsoleLogBlobUri -o tsv)" | grep -a 'RTA-ACTIVATION-CODE' | tail -n 1

The line reads RTA-ACTIVATION-CODE <code> expires=<time>: the code is the word right after RTA-ACTIVATION-CODE (the same code the console shows, without the spaces), and expires= is when it stops working (UTC; the appliance then shows a new one, so run the command again). If the line reads RTA-ACTIVATION-CODE activated, the appliance is already activated and there is nothing to send. If it reads RTA-ACTIVATION-CODE none, or its expires= time has passed, that code is no longer valid: wait a minute and run the command again for a new one, or use the screenshot. (none can also appear on a new appliance whose first registration attempt failed, before its first real code.)

If the command prints nothing, the log may not have caught up yet: wait a minute and run it again. If it still prints nothing, your image predates this feature; read the code from the console screenshot instead: Azure Portal, the VM, Help → Boot diagnostics → Screenshot.

The console shows the Appliance Registration screen: a one-time activation code (for example 4 5 D C V Y) under the prompt "Provide this code to your Sophos staging team." You do not log in to the appliance to read it.

Send that activation code to your Sophos engagement lead so they can activate the appliance on the Sophos side. Once activated, the console switches from the registration screen to the appliance's live status dashboard, and no further action is needed on your end.

Registration needs outbound 443 to get.remotetesting.secureworks.com

Registration and activation use outbound TCP/443 (HTTPS) to get.remotetesting.secureworks.com, in addition to the OpenVPN tunnel to connect.remotetesting.secureworks.com covered under Network access below. Its IP addresses are not guaranteed to stay constant, so allowlist get.* by FQDN (not by IP). If that egress is blocked, the console shows a registration error ("ensure outbound HTTPS to get.remotetesting.secureworks.com") instead of an activation code.

See the overview troubleshooting for the benign cloud-init status warning.

Network access

The appliance makes one connection to do its job: an outbound tunnel to the Sophos headend. Nothing inbound is ever required: you never open or forward any ports to the appliance.

Allow this outbound destination

Destination connect.remotetesting.secureworks.com
IP addresses 3.33.194.251 and 15.197.255.2 (static; these do not change)
Port / protocol TCP 443, carrying OpenVPN (not HTTPS)
Direction Outbound only

Allow egress on TCP/443 to that destination from the appliance's network. On a next-generation firewall or NAC-controlled network, an L3 "allow 443" rule is often not enough: Layer-7 application control, TLS/SSL decryption, or NAC can still drop the tunnel even when the port is open. See Connectivity troubleshooting for the exact firewall and NAC exceptions to request.

Troubleshooting

Deployed but something isn't right?

See this platform's troubleshooting guide for the most common issues here and how to fix them.