In my previous life as a billable cybersec consultant / technical lead, I was looking for a way to deliver phishing tests across our customers in a consistent and repeatable manner. Many options existed such as those supplied by KnowBe4, Defender for M365, Trend - but we couldn’t warrant the cost burden of those solutions until we’d proven the value of the service.

I set about finding a way to do this “on the cheap”.

GoPhish is an OSS “Phishing Toolkit” which provides facilities for creating, running, and reporting on phishing campaigns. You can read more about GoPhish and what it can do here. GoPhish was absolutely what I needed to move forward with running some phishing tests on our customers, but provisioning and configuring it was clunky and took time. GoPhish wasn’t designed with multiple target organisations in mind either.

If it doesn’t exist, build it.

I set about building GoPhish in a way that could:

  • Be spun up quickly, then spun down when the campaign was complete (saving cloud platform costs)
  • Isolate customer data into a single instance and prevent cross-contamination
  • Securely deliver landing pages with TLS
  • Send emails securely and with the greatest chance of bypassing filters (saves time on adjusting filters)
  • Allow for creativity with email/landing page domain names

Azure was the platform of choice for this work, not for any other reason than we had free credits available and I intended to use them.


I built a bash script and some Terraform code to accomplish what I had in mind. The aim of ThrowPhish is to enable rapid deployment of a GoPhish instance in Azure, along with all the bits and pieces required for it to work.

ThrowPhish will:

  • Build a linux virtual machine
  • Install GoPhish, configure it, and install it as a service
  • Apply a public IP to the VM
  • Create an Azure DNS zone for a supplied domain name
  • Obtain a Let’s Encrypt free SSL certificate and install it on the VM

It’ll also firewall the VM to allow:

  • Public access over HTTP/HTTPS (for the landing page)
  • Access to SSH and the GoPhish admin port (TCP 3333) from your current public IP

Once you’re done with a phishing campaign, you can simply run throwphish again with the destroy parameter to tear down all resources provisioned for a customer.

Preparing ThrowPhish

You can grab throwphish from the GitHub repository with the following command.

git clone


You can run ThrowPhish on Windows (with WSL) or Linux.

You’ll need to install Azure CLI and run az login to get things ready for Terraform. ThrowPhish will take care of downloading the Terraform binary for you.

Other things you’ll need:

  • A domain name
  • A SendGrid account
  • An AzureAD Service Principal

We’ll cover those next.

Register a domain

You’ll want to register a new domain - something creative, maybe customer-specific…

Think about the campaign you intend to run for the customer. Let’s say the customer uses the popular HR21/Chris21 cloud app for managing HR resources. Employees typically receive email notifications from this system for things like leave applications. Pick a domain name that’s close enough to the domain used for that customer’s app tenancy, something like as a bad example.

Don’t worry about DNS records for the domain, as ThrowPhish will take care of that for you.

Get a SendGrid account

SendGrid is an SMTP service that we’ll be using with GoPhish to send campaign emails out to the target customer/staff.

Head over to the SendGrid signup page and create a new account in the “Free” tier.

Create AzureAD Service Principal for Let’s Encrypt

An Azure Service Principal is required to allow the Terraform Let’s Encrypt (ACME) module communicate with the Azure DNS service and create the validation record for the domain.

You can easily create this using the following script:

az ad sp create-for-rbac --name TerraformAcmeDNS --skip-assignment

You can change “TerraformAcmeDNS” to whatever name works best for you.

For example:

[email protected]:~/throwphish$ az ad sp create-for-rbac --name TerraformAcmeDNS --skip-assignment
Changing "TerraformAcmeDNS" to a valid URI of "http://TerraformAcmeDNS", which is the required format used for service principal names
The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see
  "appId": "8b11d76a-ed95-4482-b4a8-deadbeeffeed",
  "displayName": "TerraformAcmeDNS",
  "name": "http://TerraformAcmeDNS",
  "password": "fbv_rK6TK61aUNotTodaySatan_NZD",
  "tenant": "3d02f87c-dd4c-4b6f-bee6-deadbeeffeed"

Save the appId and password for later.

Build the customer config

ThrowPhish uses json-based configuration files representing a ‘customer’ to determine how to build the Azure resources.

In the /config folder of the repo you’ll find domain.json.example

build one of these for your domain/customer

Edit the fields in the file using the following table as a reference.

domainThe domain you registered for use with this customer / GoPhish instance (eg.
hostnameHostname for the GoPhish server. This sets the server name and results in the full FQDN for the landing page/admin portal. (eg. gophish which results in
customerThe name of the customer this instance is being built for. This is used for tagging Azure resources. (eg. Acme Corp)
sendgrid_serverYou can leave this as unless you’ve got a reason to change it.
sendgrid_userFill in your SendGrid username.
sendgrid_passFill in your SendGrid password.
support_emailEnter your email address. This is used for the SSL Certificate registration.
azure_client_idThe Azure service principal appId you created earlier.
azure_client_secretThe App Password (password) for the service principal you created earlier.

Once you’re done, save the file as <customername>.json in the /config folder.

Running ThrowPhish

Now that all the prep work is done, we’re ready to run the script and get things moving!

The script has a few parameters, detailed below:

[email protected]:~/throwphish$ ./

 _____ _                 _____ _   _     _
|_   _| |_ ___ ___ _ _ _|  _  | |_|_|___| |_
  | | |   |  _| . | | | |   __|   | |_ -|   |
  |_| |_|_|_| |___|_____|__|  |_|_|_|___|_|_|

ERROR: Invalid number of parameters

Description: Creates and destroys GoPhish campaigns.
Usage: [customer_name] [command]

 - customer name, matching name of config file in the config/ directory

 - one of the following: createdns, createvm, destroy

command description

createdns - Creates Azure RG, prereq networking, and DNS Zone
            Returns a list of DNS Nameservers to be applied at the
            domain registrar prior to running 'createvm'

createvm -  Creates a LetsEncrypt certificate and the GoPhish VM

destroy -   Destroys all Azure resources created for the customer

First ThrowPhish will prepare Azure for the GoPhish instance and build a DNS zone. Once this is complete, you’ll go to your registrar and configure the domain to use the Azure DNS Nameservers.

Run the following command from the repo folder, replacing <customername> with the name of the JSON file you created earlier (without the .json on the end, of course).

## Make the script executable
chmod +x

## Run the first stage to prepare Azure Resource Group, DNS Zone, and Networking
./ <customername> createdns

This will take a little while, but once complete it’ll spit out the nameserver FQDNs. Log into your registrar and configure the domain to use these nameservers - you might need to wait a couple hours before proceeding to Step 2 to allow the nameserver change to replicate through DNS.

When you’re ready, run throwphish with the createvm parameter to complete the setup.

./ <customername> createvm

This does the following:

  • Validates the domain, and creates a publicly-trusted SSL certificate for the GoPhish instance
  • Creates the Azure VM, and installs/configures GoPhish

Once complete, GoPhish will be ready for use! Simply browse to and log into the GoPhish admin panel with the default credentials.


I’m keen to hear all feedback on this project. It’s not something I’m paying much attention to at the moment, but if something is broken or you’re having issues - I’m happy to help out. You can use the comments form below, or reach out to me on twitter/email/whatever.

Alternatively, feel free to fork the repository and submit some pull requests!