Ax: Building a Multi-Cloud Distributed Scanning Framework with Shell Scripts and Packer
Hook
A single shell command can spin up 100 cloud instances across three continents, distribute a vulnerability scan across them, collect the results, and tear everything down—all in under 15 minutes and for less than $20.
Context
Security reconnaissance at scale has always been a resource problem. When you're testing thousands of domains or scanning entire IP ranges, a single machine becomes the bottleneck. Traditional approaches involved maintaining expensive dedicated infrastructure or cobbling together brittle SSH loops to distribute work. Cloud providers solved the compute availability problem, but created a new one: orchestration complexity. Each provider has different APIs, instance types, and billing models.
Ax (formerly Axiom) emerged from the bug bounty and penetration testing communities where practitioners routinely need to run tools like Masscan, Nuclei, or Subfinder against massive target scopes. The framework's core insight is treating cloud instances as disposable workers: create them on-demand from pre-baked images, execute distributed workloads, and immediately destroy them. By wrapping HashiCorp Packer and cloud provider APIs in shell utilities, Ax provides a unified interface across nine different cloud platforms—from AWS and Azure to smaller providers like Hetzner and Scaleway. It's infrastructure-as-code for offensive security workflows, where the infrastructure lives only as long as the scan does.
Technical Insight
Ax's architecture revolves around three core components: image building with Packer, fleet management through cloud APIs, and workload distribution via modules. The genius lies in how these pieces compose without rigid abstractions.
Image building starts with Packer provisioners that create base images loaded with security tools. You define a JSON file specifying which tools to install, then Packer compiles this into provider-specific machine images (AMIs for AWS, snapshots for DigitalOcean, etc.). This means when you spawn 50 instances, they boot in seconds rather than spending 10 minutes installing dependencies. Here's a simplified module definition:
# modules/nuclei-scan.json
{
"name": "nuclei-distributed",
"author": "your-name",
"command": "nuclei -l input -t ~/nuclei-templates/ -o output.txt",
"ext": "txt",
"split_by_line": true
}
The split_by_line directive tells Ax to divide your input file across the fleet. If you have 10,000 domains and 50 instances, each gets 200 domains. The framework handles file distribution, command execution, and result aggregation automatically.
Fleet management uses a provider abstraction layer written entirely in shell. Commands like ax-fleet create -i 25 --provider do spawn instances, while ax-fleet exec 'command' runs arbitrary commands across all active instances via SSH multiplexing. Under the hood, this generates provider-specific API calls:
# Simplified DigitalOcean fleet creation logic
for i in $(seq 1 $instance_count); do
doctl compute droplet create "ax-$RANDOM" \
--image "$base_image_id" \
--size "$instance_size" \
--region "$region" \
--ssh-keys "$ssh_key_id" \
--wait &
done
wait
The ampersand and wait commands parallelize instance creation. For AWS, the equivalent uses aws ec2 run-instances; for GCP, gcloud compute instances create. Ax maintains a local state file tracking active instances, their IPs, and metadata.
Workload distribution leverages GNU Parallel and SSH. When you run ax-scan nuclei-distributed targets.txt, the framework splits the input file, scps chunks to each instance, executes the scan command, and continuously streams results back:
# Core distribution pattern
ax-fleet ls | parallel -j $parallelism ssh {} "nuclei -l /tmp/chunk-{#} -o /tmp/output-{#}"
ax-fleet ls | parallel -j $parallelism scp {}:/tmp/output-{#} ./results/
The module system is just structured conventions. You can create custom modules by dropping shell scripts or JSON configs into the modules directory. Need to distribute a custom Rust binary? Write a module that scps it to each instance and executes it. The framework doesn't care about the payload—it's purely orchestration.
One clever design choice: Ax stores cloud provider credentials and configuration in ~/.axiom/accounts/, letting you switch contexts with ax-account select aws-profile. This mirrors kubectl's context switching and makes it trivial to run the same scan across multiple providers simultaneously, useful for geographic distribution or cost optimization. The shell-based implementation means extending Ax is as simple as writing bash functions—no compilation or complex plugin systems.
Gotcha
Shell-based orchestration hits real limits at scale. Error handling in distributed systems is notoriously difficult, and bash scripts lack the structured exception handling of compiled languages. If one instance in your fleet fails mid-scan—maybe it gets rate-limited or the cloud provider has a hiccup—there's no automatic retry logic beyond what you script yourself. You'll find yourself grep'ing through logs and manually tracking which chunks succeeded.
Cost management requires vigilance. The framework makes spinning up 100 instances trivially easy, but cloud bills aren't trivial. A forgotten fleet running overnight can burn through hundreds of dollars. Ax includes teardown commands, but if your terminal session dies or you lose network connectivity during a scan, orphaned instances keep running and accruing charges. There's no built-in billing safeguards or maximum spend limits. Additionally, the Packer build process itself incurs costs—you're running temporary instances to generate images—and these can add up during development.
The steep learning curve deserves mention. Ax assumes you understand Packer provisioners, cloud IAM policies, SSH key management, and network security groups. The documentation provides examples, but troubleshooting "why won't my instances boot" requires debugging across multiple layers: Packer configs, cloud provider APIs, and network connectivity. If you've never worked with infrastructure-as-code or haven't administered Linux systems, the cognitive overhead is substantial. This isn't a GUI application with helpful error messages—it's a power tool that expects operator expertise.
Verdict
Use if: You're conducting large-scale security reconnaissance or vulnerability assessments where parallelization directly translates to time savings, you're already comfortable with cloud infrastructure and command-line workflows, and you need flexibility to run arbitrary scanning tools without being locked into a specific vendor's platform. Ax shines for bug bounty hunters who scan thousands of assets monthly, red teams distributing network enumeration across geographic regions, or security researchers running experimental tools at scale. Skip if: You're looking for a managed SaaS solution with customer support, you need audit trails and compliance features for enterprise environments, your cloud budget is constrained (single-instance parallelization with tools like Interlace might suffice), or you prefer opinionated frameworks that guide you through workflows rather than providing raw building blocks. Also skip if you're uncomfortable with the operational burden of managing ephemeral infrastructure—the freedom Ax provides comes with responsibility for cost management, error handling, and security hygiene.