Skip to content

Microsoft Azure

Running ARROW on Azure means uploading a disk image, registering it as a managed image, and launching VMs from it. Your Azure build downloads as a ZIP containing the disk image and arrow-deploy, a single tool that does all of it.


FileWhat it is
*.vhdThe disk image. Fixed-size, usually 120 GB.
arrow-deploy-linux-amd64The deploy tool for Linux
arrow-deploy-darwin-arm64The deploy tool for macOS (Apple silicon)
arrow-deploy-windows-amd64.exeThe deploy tool for Windows
*-deployment.txtA summary of this build and the manual commands
DEPLOY-README.txtQuick reference
deploy-to-azure.sh, Deploy-To-Azure.ps1The previous scripts, still included during the transition

You need the Azure CLI and AzCopy, and you need to be signed in with az login.

AzCopy matters more than it looks. The disk is fixed-size but mostly empty, and AzCopy uploads only the pages that hold real data, so you transfer roughly 30 to 50 GB instead of the full 120 GB.

You also need a resource group and a storage account in the region you are deploying to.

doctor verifies your prerequisites and creates nothing:

Terminal window
chmod +x arrow-deploy-linux-amd64
./arrow-deploy-linux-amd64 doctor --cloud azure

arrow-deploy doctor output showing all Azure prerequisites passing

Anything reported as FAIL tells you what to fix. Resolve it before importing, so a long upload does not fail at the end for a reason you could have seen in a second.


This uploads the disk and registers it as a managed image. It is the slow step; the upload dominates.

Terminal window
./arrow-deploy-linux-amd64 import \
--cloud azure \
--image kali-arrow-yourclient-azure.vhd \
--image-name arrow-kali \
--resource-group my-resource-group \
--storage-account mystorageaccount

The command prints the image ID it created. You need it for the next step.

Terminal window
./arrow-deploy-linux-amd64 launch \
--cloud azure \
--image-id <id-from-import> \
--vm-name arrow-kali-01 \
--resource-group my-resource-group \
--size Standard_D4s_v3

The VM’s public address is printed when it is ready.

The VM has two separate accounts, and it matters which one you use.

AccountWho owns itPassword
azureuserAzure creates and owns itSet by you at launch, or your SSH key
arrowThe ARROW imageUnique to this VM, shown in the ARROW Portal

Use arrow with the password from the Portal for console access and manual configuration. The VM connects back to ARROW on its own as soon as it has network access, so most of the time you do not need to sign in at all.


A deployed VM’s hostname is the name shown on its device record in the ARROW Portal, not the Azure resource name. This is deliberate, because the Portal name is the identity your team works with, so the image keeps it and ignores the name Azure would otherwise apply.

Name the Azure resource whatever suits your subscription. It does not change the hostname inside the VM.


cleanup reports what it would remove and deletes nothing until you add --yes:

Terminal window
./arrow-deploy-linux-amd64 cleanup --cloud azure --resource-group my-resource-group

arrow-deploy cleanup reporting no ARROW-managed resources found

Once you are happy with the list:

Terminal window
./arrow-deploy-linux-amd64 cleanup --cloud azure --resource-group my-resource-group --yes

It only removes resources it created, which it identifies by its own marker. Narrow it further with --name-prefix when a group holds more than one deployment.


arrow-deploy help output listing the commands and common flags

CommandWhat it does
doctorChecks prerequisites, creates nothing
importUploads a disk image and registers it as a managed image
launchCreates a VM from an imported image
cleanupRemoves resources the tool created
versionPrints the version
FlagApplies toMeaning
--cloud azureallTarget cloud, always required
--imageimportPath to the .vhd
--image-nameimportName to register the image under
--resource-groupimport, launch, cleanupAzure resource group
--storage-accountimportStorage account for the upload
--locationimport, launchRegion
--image-idlaunchThe ID that import printed
--vm-namelaunchName for the VM
--sizelaunchVM size, for example Standard_D4s_v3
--network, --subnetlaunchExisting virtual network and subnet
--security-grouplaunchNetwork security group
--ssh-keylaunchName of an SSH key already in Azure
--admin-usernamelaunchCloud admin account, defaults to azureuser
--name-prefixcleanupRestrict cleanup to matching names
--yescleanupActually delete; without it cleanup only reports
FlagMeaning
--dry-runReport what would happen without changing anything
--jsonMachine-readable output, for scripting
--non-interactiveNever prompt; every value must be supplied as a flag
--no-colorDisable coloured output. NO_COLOR is also honoured

Run arrow-deploy <command> --help for the full list.


--json and --non-interactive together make the tool scriptable. Nothing prompts, and every result parses:

Terminal window
IMAGE_ID=$(./arrow-deploy-linux-amd64 import \
--cloud azure --json --non-interactive \
--image kali-arrow-yourclient-azure.vhd \
--image-name arrow-kali \
--resource-group my-resource-group \
--storage-account mystorageaccount | jq -r .image_id)
./arrow-deploy-linux-amd64 launch \
--cloud azure --json --non-interactive \
--image-id "$IMAGE_ID" \
--vm-name arrow-kali-01 \
--resource-group my-resource-group

Add --dry-run first to see exactly what a script will do before it does it.


SizevCPU / RAMUse
Standard_D2s_v32 / 8 GBMinimum
Standard_D4s_v34 / 16 GBRecommended for most work
Standard_D8s_v38 / 32 GBHeavier workloads

Extraction fails partway through. The disk inside the ZIP is 120 GB even though the ZIP is around 26 GB. Free up space for the full disk size and extract again.

doctor reports azcopy missing. Install it from the AzCopy downloads. Without it the upload falls back to transferring the entire fixed-size disk.

doctor reports authentication failed. Run az login, and confirm you are on the intended subscription with az account show.

The upload is very slow. Check that doctor reports azcopy as present. Uploading a 120 GB disk instead of its 30 to 50 GB of real data is the usual cause.

The arrow password from the Portal does not work. Confirm the cloud admin account was not named arrow. If it was, Azure has reset that account’s password and the VM needs redeploying with a different admin username.

The VM’s hostname is not the Azure resource name. That is expected. The VM keeps the name from its ARROW Portal device record.


deploy-to-azure.sh and Deploy-To-Azure.ps1 are still included in the download and still work. They walk through the same flow interactively:

Terminal window
chmod +x deploy-to-azure.sh
./deploy-to-azure.sh # import, then optionally launch
./deploy-to-azure.sh --launch # launch from an image you already imported
./deploy-to-azure.sh --cleanup # find and remove ARROW images and VMs
Terminal window
.\Deploy-To-Azure.ps1
.\Deploy-To-Azure.ps1 -Launch
.\Deploy-To-Azure.ps1 -Cleanup

New deployments should use arrow-deploy. It is the same flow on every platform, it supports --dry-run and --json, and it removes what it created if something fails partway.