Skip to content

Cloud Prerequisites

BaddKharma edited this page Nov 5, 2026 · 8 revisions

Cloud Prerequisites

Two things have to be true before you run deploy.sh, and redStackPRO cannot do either for you (see Concepts, the export is the boundary):

  1. Your cloud identity can create the resources the export builds. VPCs or networks, subnets, security groups or firewall rules, instances, and elastic or static IPs, at minimum.
  2. Your machine has the tools the deploy script needs. Terraform 1.5 or newer, ssh, tar, Python 3.8 or newer, and your cloud's CLI (gcloud, aws, or az), none of which redStackPRO installs.

This page covers GCP, AWS, and Azure, the three providers tested end to end. Proxmox and ESXi are on the roadmap, not yet supported; see Providers for their status. Quota planning for AWS and GCP (Elastic IPs, vCPUs, the GCP CPUS_ALL_REGIONS cap) is covered there too. Azure's sizes and quotas are covered on this page, because the Azure deploy checks them before it applies.

What your machine needs

Always terraform 1.5 or newer, ssh, tar, and Python 3.8 or newer
GCP The Google Cloud CLI (gcloud): for the login, which deploy.sh checks with it, and for manage.sh status, start, stop, and teardown --force
AWS The AWS CLI (aws): for aws configure, which deploy.sh checks with aws sts get-caller-identity, and for manage.sh status, start, stop, and teardown --force
Azure The Azure CLI (az): for az login, and for manage.sh status, start, stop, and teardown --force. A deploy with a service principal runs without it
Never (installs itself) Ansible: the deploy installs it on the jumpbox over ssh, nothing to add on your machine

Of these, ssh and tar ship with Git for Windows and come stock on macOS and Linux; you install Terraform, a real Python 3.8+, and the cloud CLI yourself (see Getting Started). Once these are present the deploy runs the same way on all three. On Windows, run .\deploy.ps1 rather than bash deploy.sh at a PowerShell prompt: PowerShell's bash resolves to the WSL launcher, a different filesystem with a different home directory and different cloud credentials. deploy.ps1 finds Git Bash (C:\Program Files\Git\bin\bash.exe or your user-scope install) and calls it directly. If Git for Windows is not installed, deploy.ps1 says so and points at https://git-scm.com/download/win.

The deploy also looks for a working Python at python3, python, or py, in that order, and runs a quick check rather than trusting PATH: on Windows, the Microsoft Store's python3 stub is on PATH by default and merely prints an advert, so a plain command -v check would pass and then fail on the first real use. Install a real Python 3.8+ if none of the three runs.

Cloud identity permissions

No provider's Terraform module creates an IAM role, a service account, or (on Azure) a managed identity or role assignment for the export's own use (the one exception is auto_stop, on AWS only, see below). The permissions your identity needs map directly to the resource types the modules create.

AWS

The AWS modules create:

  • aws_vpc, aws_internet_gateway, aws_subnet, aws_route_table, aws_route_table_association, aws_route
  • aws_nat_gateway and aws_eip (one per network that needs NAT, one per host that takes a public address: the jumpbox, and on the attack side, the redirector)
  • aws_security_group and aws_vpc_security_group_ingress_rule (one group per host)
  • aws_instance, plus the aws_ami data source used to resolve the image
  • aws_key_pair (one per export, from your ssh_public_key)
  • aws_vpc_peering_connection and aws_route (when a topology peers two networks, for example redStack's main network and its redirector network)

Simple path: attach the AmazonEC2FullAccess managed policy to the IAM user or role you configure with aws configure, and grant the auto-stop permissions below as well. AmazonEC2FullAccess covers every resource type above, but not the auto-stop role and schedule, and every shipped blueprint turns auto-stop on.

Scoping a custom policy: grant ec2:* (or the narrower set of Create/Describe/Delete/Modify actions for VPCs, subnets, route tables, internet gateways, NAT gateways, security groups, security group rules, instances, key pairs, Elastic IPs, and VPC peering connections) on the account and region you deploy into.

The one exception, auto_stop. If a topology sets an auto-stop (Stop at a time, or Stop after a number of hours; every shipped blueprint stops 8 hours after the first apply), the export also creates aws_iam_role, aws_iam_role_policy, and aws_scheduler_schedule (an EventBridge Scheduler schedule that stops the range's instances on a cron), each named <prefix>-auto-stop-<random suffix>. Grant these in addition to the EC2 permissions above:

  • to create them on the first apply: iam:CreateRole, iam:PutRolePolicy, iam:PassRole, and scheduler:CreateSchedule
  • to read them on every later plan and re-run: iam:GetRole, iam:GetRolePolicy, iam:ListRolePolicies, iam:ListAttachedRolePolicies, and scheduler:GetSchedule, plus scheduler:UpdateSchedule when the stop time changes
  • to delete them at teardown: iam:DeleteRolePolicy, iam:ListInstanceProfilesForRole, iam:DeleteRole, and scheduler:DeleteSchedule

This is deliberately the export's only IAM-touching resource; an identity that cannot create roles fails the apply here, by name, rather than partway through something broader. If your account restricts IAM role creation, turn auto-stop off in Topology settings before you export, and the apply needs nothing beyond EC2.

GCP

The GCP modules create:

  • google_compute_network, google_compute_subnetwork
  • google_compute_router and google_compute_router_nat (one pair per network segment that needs NAT)
  • google_compute_address (a reserved static IP for the jumpbox, and for a redirector on the attack side)
  • google_compute_instance
  • google_compute_firewall (one set per segment, plus intra-segment rules)
  • google_compute_network_peering (when a topology peers two networks)
  • google_compute_resource_policy, GCP's own auto-stop schedule, when a topology sets one. Unlike AWS this is a Compute Engine resource, not an IAM one, so it needs no extra role.

All of the above are Compute Engine resources. Grant your account roles/compute.admin on the project. The instance resource does not attach a service account to the VMs it creates (no service_account block in the module), so roles/iam.serviceAccountUser is not required for a stock deploy; only add it if you customize the topology to attach a service account yourself.

Enable the Compute Engine API before the first apply:

gcloud services enable compute.googleapis.com --project <project-id>

The export touches no other GCP API: no Cloud DNS, no IAM API calls beyond what compute.admin already covers. A redirector's DNS record is something you create at your own registrar, not a GCP resource; see Deploying a Range.

The GCP quota that actually bites: CPUS_ALL_REGIONS

GCP caps running Compute Engine vCPUs per project, not per region, under CPUS_ALL_REGIONS (default 32). A topology with enough hosts can exceed this well before any single-region or machine-type limit does, which makes it the real ceiling on how large a topology you can deploy without asking for more. Check and raise it under IAM & Admin > Quotas in the console, filtered to the Compute Engine API, before a large deploy. See Providers for the full GCP quota table (external IPs, project/billing setup) and the AWS equivalent (Elastic IPs, vCPUs, the Kali Marketplace subscription).

Azure

Each range is one resource group, named by resource_group in deploy.tfvars (default {prefix}-{topology name}-rg, for example def-goad-light-rg). The Azure modules create the group and everything else inside it:

  • azurerm_resource_group
  • azurerm_virtual_network, azurerm_subnet
  • azurerm_network_security_group, azurerm_network_security_rule, and azurerm_subnet_network_security_group_association
  • azurerm_nat_gateway, its azurerm_public_ip, and the azurerm_nat_gateway_public_ip_association and azurerm_subnet_nat_gateway_association that wire it in (one NAT gateway per virtual network that needs one)
  • azurerm_public_ip (a static address for the jumpbox, and for a redirector on the attack side)
  • azurerm_network_interface, azurerm_linux_virtual_machine, azurerm_windows_virtual_machine
  • azurerm_virtual_machine_extension (a Custom Script Extension on Windows hosts, which turns on WinRM for provisioning and sets up a Windows operator)
  • azurerm_virtual_network_peering (when a topology peers two networks)
  • azurerm_dev_test_global_vm_shutdown_schedule, Azure's per-VM auto-stop schedule, when a topology sets one. It is a Microsoft.DevTestLab resource, not an identity one, so it needs no extra role, but that resource provider has to be registered (see Azure resource providers below).

Simple path: give the account or service principal you deploy as the Contributor role on the subscription. It covers every resource type above, and registering resource providers. Grant it at subscription scope: the export creates its own resource group, so a role scoped to an existing resource group cannot create it.

Azure resource providers

A subscription creates a resource type only once its resource provider is registered. The export needs two groups, and the preflight (below) treats them differently:

  • Registered for you. Microsoft.Compute, Microsoft.Network, Microsoft.Storage, and Microsoft.ManagedIdentity, plus the rest of the set Terraform's azurerm provider registers itself when it starts (Microsoft.Authorization, Microsoft.CostManagement, Microsoft.MarketplaceOrdering, Microsoft.Resources). This works when your identity may register providers, which Contributor can. The preflight only warns if one of these is not registered.
  • Yours to register. Microsoft.DevTestLab, needed when the topology sets auto_stop (every shipped blueprint does). azurerm does not register it, and without it apply would fail after the VMs exist, so the preflight stops the deploy before anything is created.

To register them yourself, for example on an identity that may not:

az provider register --namespace Microsoft.Compute --wait
az provider register --namespace Microsoft.Network --wait
az provider register --namespace Microsoft.Storage --wait
az provider register --namespace Microsoft.ManagedIdentity --wait
az provider register --namespace Microsoft.DevTestLab --wait

Check one:

az provider show --namespace Microsoft.DevTestLab --query registrationState -o tsv

It prints Registered when it is done.

Azure sizes and quotas

Azure subscriptions differ widely in what they may allocate, and this is the part of an Azure deploy most likely to stop you. Azure caps vCPUs per region twice: a total regional quota, and a quota per VM size family. It also caps public IPs per region. A subscription can be barred from a size, or a whole size family, in a region, which apply reports as SkuNotAvailable.

What we have seen on real subscriptions: a new or trial subscription often has only 4 to 10 regional vCPUs and per-family quotas around 10, and some subscriptions are barred from whole size families. The full redstack needs 16 vCPUs. At the default sizes the Azure-tested blueprints need:

Blueprint Hosts vCPUs Public IPs
minimalc2-cli 4 8 3
redstack 8 16 3
minilab 3 6 2
goad-light 4 8 2
goad-wazuh 6 14 2

The public IPs are the jumpbox, the redirector on an offense range, and one per NAT gateway. See your current usage and limits:

az vm list-usage --location eastus2 -o table

Raise a quota in the Azure portal under Quotas > Compute (vCPUs) or Quotas > Networking (public IPs), for the location you deploy into.

VM sizes per role. Each host takes the size of its role, the node kind. The defaults are the AMD v5 family:

Role Default size
jumpbox, redirector, teamserver, collector, operator, dc, srv Standard_D2as_v5 (2 vCPUs)
wks Standard_B2as_v2 (2 vCPUs, burstable)
siem Standard_D4as_v5 (4 vCPUs)

deploy.tfvars ships a commented vm_sizes map listing the roles in that topology with their defaults. To change some, uncomment the first and last lines and keep only the roles you change; a partial map overrides only the roles it names, and the rest keep their default:

vm_sizes = {
  jumpbox = "Standard_D2s_v5"
}

Check whether a size is offered and unrestricted for your subscription in a location:

az vm list-skus --location eastus2 --size Standard_D2as_v5 -o table

The Azure preflight

You rarely need the two commands above, because the deploy checks for you. After terraform init and before terraform apply, deploy.sh runs azure_preflight.py, which ships in the export. It is read only and asks your subscription, for the location in deploy.tfvars:

  • whether each VM size is offered there and not restricted for you (a size restricted only in some zones passes with a warning, since the export places VMs with no zone)
  • whether the total regional vCPU quota has room
  • whether each size family's vCPU quota has room
  • whether the public IP quota has room
  • whether the resource providers above are registered
  • whether the resource group it would create already exists outside this export's Terraform state

It counts the VMs and public IPs this export's own Terraform state already holds, so a re-run does not count the range against itself twice. It asks as the identity apply will use: the service principal when ARM_CLIENT_ID, ARM_CLIENT_SECRET, and ARM_TENANT_ID are set, otherwise your az login.

On a problem it stops before anything is created and says what to change: a vm_sizes block that fits your quota and the sizes your subscription may use, the quota to raise, the az provider register command to run, or another resource_group to set. If it cannot reach Azure (no network, no credentials, a 403) it warns and the deploy continues; apply then reports any limit itself. Skip it entirely with:

macOS, Linux, or Git Bash:

REDSTACKPRO_SKIP_PREFLIGHT=1 bash deploy.sh

Windows PowerShell:

$env:REDSTACKPRO_SKIP_PREFLIGHT = '1'; .\deploy.ps1

Run it on its own at any time, from the export root:

python azure_preflight.py .

On a pass, the deploy shows one summary line; run it on its own for the full table.

Windows client licensing on Azure

Windows 10 and 11 hosts on Azure boot Microsoft's client images, and a new workstation defaults to Windows 11 (see Windows workstations in Providers). Microsoft licenses Windows client VMs on Azure only for customers with a qualifying license, for example Windows Enterprise E3 or E5 per user, a Microsoft 365 plan that includes it, or a Visual Studio subscription for dev and test. redStackPRO does not set license_type on these VMs, so license eligibility is yours to confirm; see Microsoft's page. When a topology has a Windows 10 or 11 host, the export's DEPLOYMENT-GUIDE.md carries the same note. On GCP and AWS these hosts boot Windows Server 2022, so this does not apply there.

Authenticate: AWS CLI

If you do not already have the AWS CLI, install it from the official per-OS instructions: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html

On Windows, winget installs it (PowerShell):

winget install -e --id Amazon.AWSCLI

The install only updates PATH for new windows, so open a new PowerShell window in the export folder before you log in.

Then configure it:

aws configure

This prompts for an access key ID, a secret access key, a default region, and a default output format, and writes them to ~/.aws/credentials and ~/.aws/config (C:\Users\<you>\.aws\ on Windows). Confirm it worked:

aws sts get-caller-identity

That should print your account id and the IAM identity you just configured. Terraform reads the same standard AWS credential chain the CLI does (environment variables, then ~/.aws/credentials), so nothing further is needed for terraform apply to pick up what aws configure just wrote. See the full precedence order: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html

Authenticate: gcloud CLI

If you do not already have it, install the gcloud CLI from the official per-OS instructions: https://cloud.google.com/sdk/docs/install

On Windows, winget installs it (PowerShell):

winget install -e --id Google.CloudSDK

The install only updates PATH for new windows, so open a new PowerShell window in the export folder before you log in.

Then run both of these:

gcloud auth login
gcloud auth application-default login

Both matter, and only the first is obvious when it lapses. gcloud auth login signs in the CLI itself; gcloud auth application-default login writes Application Default Credentials (ADC), a separate credential file that Terraform's Google provider (and the generated deploy.sh) actually reads. deploy.sh checks for ADC before it applies anything. If it is missing, it stops with GCP credentials are not set. and prints both login commands, each on its own line, rather than a raw Terraform authentication error. See the ADC docs: https://cloud.google.com/docs/authentication/provide-credentials-adc

Point the CLI at the right project:

gcloud config set project <project-id>

If you do not have a project yet, or need to enable billing on one, see Providers for the gcloud projects create / gcloud billing projects link commands.

Authenticate: Azure CLI or a service principal

deploy.sh accepts either an az login session or a service principal in the environment. Use az login on your own machine; use a service principal for a shared or headless identity.

If you do not already have it, install the Azure CLI from the official per-OS instructions: https://learn.microsoft.com/cli/azure/install-azure-cli

On Windows, winget installs it (PowerShell):

winget install -e --id Microsoft.AzureCLI

The install only updates PATH for new windows, so open a new PowerShell window in the export folder before you log in.

With az login:

az login
az account set --subscription <subscription-id>
az account show --query id -o tsv

The last command prints the subscription id. Put it in deploy.tfvars as subscription_id. Terraform's azurerm provider reads the az login session directly, so nothing further is needed for apply.

With a service principal: create one with Contributor on the subscription:

az ad sp create-for-rbac --name redstackpro --role Contributor --scopes /subscriptions/<subscription-id>

It prints an appId, a password, and a tenant. Export them, with the subscription id, before you run deploy.sh (bash or Git Bash):

export ARM_CLIENT_ID=<appId>
export ARM_CLIENT_SECRET=<password>
export ARM_TENANT_ID=<tenant>
export ARM_SUBSCRIPTION_ID=<subscription-id>

PowerShell, before .\deploy.ps1:

$env:ARM_CLIENT_ID="<appId>"
$env:ARM_CLIENT_SECRET="<password>"
$env:ARM_TENANT_ID="<tenant>"
$env:ARM_SUBSCRIPTION_ID="<subscription-id>"

When these are set, azurerm uses the service principal ahead of any az login, and so does the preflight. Set subscription_id in deploy.tfvars as well. Treat the secret like any credential: it never goes in the export, and deploy.tfvars has no field for it.

manage.sh needs az either way. status, start, and stop run through the Azure CLI (az vm list, az vm start, az vm deallocate), so the CLI has to be installed and logged in (az login, or az login --service-principal with the same credentials). teardown --force deletes the whole resource group with az group delete; without az it says so and falls back to a plain terraform destroy. A plain teardown is terraform destroy and needs no az. See Managing a range in Deploying a Range.

Proxmox, ESXi

GCP, AWS, and Azure are tested end to end, and the permission guidance above covers those three. Proxmox and ESXi are on the roadmap, not yet supported, and cannot allocate a public address on their own. See Providers for current status before planning a deploy on either.

Clone this wiki locally