Repository navigation
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):
- 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.
-
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, oraz), 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.
| 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.
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.
The AWS modules create:
-
aws_vpc,aws_internet_gateway,aws_subnet,aws_route_table,aws_route_table_association,aws_route -
aws_nat_gatewayandaws_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_groupandaws_vpc_security_group_ingress_rule(one group per host) -
aws_instance, plus theaws_amidata source used to resolve the image -
aws_key_pair(one per export, from yourssh_public_key) -
aws_vpc_peering_connectionandaws_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, andscheduler:CreateSchedule - to read them on every later plan and re-run:
iam:GetRole,iam:GetRolePolicy,iam:ListRolePolicies,iam:ListAttachedRolePolicies, andscheduler:GetSchedule, plusscheduler:UpdateSchedulewhen the stop time changes - to delete them at teardown:
iam:DeleteRolePolicy,iam:ListInstanceProfilesForRole,iam:DeleteRole, andscheduler: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.
The GCP modules create:
-
google_compute_network,google_compute_subnetwork -
google_compute_routerandgoogle_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.
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).
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, andazurerm_subnet_network_security_group_association -
azurerm_nat_gateway, itsazurerm_public_ip, and theazurerm_nat_gateway_public_ip_associationandazurerm_subnet_nat_gateway_associationthat 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.
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
Contributorcan. 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 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
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 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.
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
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.
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.
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.