Security Azure

Prowler on Ubuntu 24.04 LTS on Azure

| Product: Prowler on Ubuntu 24.04 LTS by cloudimg

This image ships Prowler 5.35.0 on Ubuntu 24.04 LTS, ready to assess your cloud accounts from the first boot of every deployed virtual machine. Prowler is the open source security assessment tool that runs hundreds of checks against a cloud account and reports exactly which controls pass, which fail, and which compliance framework requirements each one maps to.

It covers Azure, AWS, Google Cloud, Kubernetes, Microsoft 365, GitHub and more from one command line, and maps its findings onto dozens of published frameworks including CIS, PCI DSS, HIPAA, SOC 2, ISO 27001, NIST and FedRAMP.

What this appliance is, precisely

Read this section before you deploy. It is short, and it is the difference between an appliance that does what you expect and one that quietly does not.

This image ships the Prowler CLI. That is a deliberate choice, and you should know it was made rather than assume otherwise. The Prowler project also publishes Prowler App, a web interface backed by a Django API, a PostgreSQL database, a Valkey cache and a set of Celery workers, normally run as a multi container Docker Compose stack. This image does not include Prowler App. There is no web interface, no dashboard, no login page and no listening port other than SSH.

That trade is intentional and works in your favour on a security appliance:

  • The attack surface stays at zero. Prowler App would add four network listeners and a database to a machine whose entire job is to tell you about your security posture. The CLI binds nothing at all, which you can verify yourself in section 3.
  • It runs comfortably on a small VM. The recommended size is a 2 vCPU B2s. The App stack would not be happy there.
  • Nothing is withheld. The CLI is not a reduced edition. It runs every check, every provider and every compliance framework Prowler ships. The App is a different way to look at the same engine, not a bigger engine.

If what you want is a hosted multi user dashboard with scan history and role based access, that is what the Prowler project's own hosted service is for, and no configuration of this image turns it into that.

Prowler measures and reports. It does not change anything. Running an assessment against your Azure subscription alters nothing in that subscription. This appliance has no remediation mode enabled and never modifies a cloud account.

This image ships with no cloud credentials, deliberately

This is the single most important thing to understand about deploying a cloud security scanner as a machine image.

Prowler assesses your cloud account, so it needs your credentials. Any image that arrived with working credentials baked in would either be useless to you or a serious security problem for whoever built it. So this one ships with none:

  • no ~/.aws directory, no AWS access keys, no session tokens,
  • no ~/.azure profile, no service principal secret, no tenant configuration,
  • no Google service account JSON, no gcloud application default credentials,
  • no Kubernetes kubeconfig.

That is verified at build time by a filesystem wide sweep that looks for both credential files and credential shapes in file contents, and it is re assessed on a freshly booted virtual machine created from the finished image before that image is ever published.

Section 5 shows you how to give Prowler access to your own account. The recommended path on Azure is a managed identity, which means no secret is ever written to the VM at all.

You do not need credentials to prove the appliance works. Section 4 runs a complete, genuine assessment with no cloud account involved at all.

1. Deploy the virtual machine

Deploy from the Azure Marketplace listing, or from the Azure CLI. A Standard_B2s is sufficient for assessing a single subscription; step up to a Standard_B2ms or larger if you plan to assess a large estate or many accounts in one run.

az group create --name prowler-rg --location eastus

az vm create \
  --resource-group prowler-rg \
  --name prowler-vm \
  --image <the Prowler on Ubuntu 24.04 LTS by cloudimg marketplace image URN> \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Prowler needs outbound HTTPS to reach the cloud APIs it assesses. It needs no inbound access at all beyond the SSH you use to administer it, so do not open any other port.

2. Connect to the virtual machine

ssh azureuser@<vm-ip>

On first boot the appliance runs a baseline assessment of its own, using the credential free example fixture described in section 4, and writes a per instance note. Read it first:

sudo cat /root/prowler-instance.txt

That note contains no secrets, because there are none to contain.

3. Confirm the scanner and its dependency

prowler --version
trivy --version | head -2
dpkg-query -W -f='${Package} ${Version}\n' trivy

Expected output:

Prowler 5.35.0 (You are running the latest version, yay!)
Version: 0.72.0
trivy 0.72.0

Why trivy is here. Prowler's infrastructure as code provider does not bundle its own scanning engine: it shells out to trivy. Without trivy on the path, the one capability this appliance can demonstrate with no cloud credentials would not work at all. It is installed from the vendor's own apt repository rather than dropped into /usr/local/bin, specifically so that Ubuntu's unattended upgrades can patch it. Shipping an unpatchable binary on a security appliance would be self defeating.

Confirm the appliance introduces no network listener of its own:

ss -tln

You should see only SSH on port 22 and the local systemd resolver on 127.0.0.53. Nothing else. That is the whole network posture of this image.

Prowler 5.35.0 and its apt managed trivy dependency confirmed on Ubuntu 24.04, alongside the listening port check showing only SSH

4. Prove it works, with no credentials at all

This is the section to run first, before you configure any cloud access. It proves the scanner genuinely produces findings rather than merely starting up.

The image ships two example fixtures. One is deliberately misconfigured Terraform, clearly labelled and never deployed by anything on this machine. The other is empty, and exists as a control.

ls /opt/prowler/examples/
head -12 /opt/prowler/examples/insecure-terraform/main.tf

Now run a real assessment against the misconfigured one. Note the trailing echo, and read the next paragraph before you wonder why it is there:

prowler iac --scan-path /opt/prowler/examples/insecure-terraform --no-banner; echo "exit code: $?"

Expected output, ending with a summary like this:

-> Scanning local IaC directory:
  · Directory: /opt/prowler/examples/insecure-terraform
  · Scanners: misconfig, secret
  · Authentication method: No auth

Overview Results:
╭────────────────────┬────────────────────┬────────────────╮
│ 18.29% (15) Failed │ 81.71% (67) Passed │ 0.0% (0) Muted │
╰────────────────────┴────────────────────┴────────────────╯

Directory /opt/prowler/examples/insecure-terraform Scan Results (severity columns are for fails only):
╭────────────┬───────────┬───────────┬────────────┬────────┬──────────┬───────┬─────────╮
│ Provider   │ Service   │ Status    │   Critical │   High │   Medium │   Low │   Muted │
├────────────┼───────────┼───────────┼────────────┼────────┼──────────┼───────┼─────────┤
│ iac        │ terraform │ FAIL (15) │          1 │      9 │        1 │     4 │       0 │
╰────────────┴───────────┴───────────┴────────────┴────────┴──────────┴───────┴─────────╯
exit code: 3

Note Authentication method: No auth. No cloud account was contacted and no credential was used.

Now read that exit code carefully, because it is not what you might assume. Prowler exits 3 when a scan completes successfully and found failing checks. It exits 0 when a scan completes and everything passed, and 1 on a genuine error.

So exit code: 3 here is the correct result: the fixture is deliberately insecure, so findings are exactly what should happen. This trips people up constantly when Prowler is wired into CI, because a shell with set -e, or a pipeline step that treats any non zero exit as a failure, will report a perfectly good scan as a broken build. If you want failing findings to leave the exit code at 0, pass -z (or --ignore-exit-code-3).

Now the control. Scanning the empty fixture must find nothing:

prowler iac --scan-path /opt/prowler/examples/empty-terraform --no-banner > /dev/null 2>&1; echo "exit code: $?"

That prints exit code: 0. A scanner that reported findings against an empty directory would be fabricating them, and running these two scans back to back is how you satisfy yourself that it is not.

A real credential free Prowler assessment of the shipped example fixture, reporting 15 failed and 67 passed checks with authentication method No auth

5. Give Prowler access to your own cloud

Pick the option that matches the account you want to assess. All of these are things you do on your own tenancy; nothing here is preconfigured.

Azure with a managed identity (recommended). No secret is ever stored on the virtual machine.

Assign a system assigned managed identity to the VM and grant it read only access. Prowler needs the Reader role on the subscriptions you want to assess, plus Security Reader for the security posture checks. Some identity checks additionally require Microsoft Graph read permissions on the directory.

az vm identity assign --resource-group prowler-rg --name prowler-vm

PRINCIPAL_ID=$(az vm show --resource-group prowler-rg --name prowler-vm \
  --query identity.principalId -o tsv)
SUB_ID=$(az account show --query id -o tsv)

az role assignment create --assignee "$PRINCIPAL_ID" \
  --role "Reader" --scope "/subscriptions/$SUB_ID"
az role assignment create --assignee "$PRINCIPAL_ID" \
  --role "Security Reader" --scope "/subscriptions/$SUB_ID"

Then, on the virtual machine:

prowler azure --managed-identity-auth

Azure interactively, if you would rather sign in as yourself:

az login
prowler azure --az-cli-auth

AWS with an instance role (recommended). Attach an IAM role to the virtual machine with the AWS managed SecurityAudit and ViewOnlyAccess policies, then simply:

prowler aws

Google Cloud:

gcloud auth application-default login
prowler gcp

Kubernetes, against a cluster your kubeconfig can reach:

prowler kubernetes --kubeconfig-file ~/.kube/config

Whichever you choose, grant read only access. Prowler never needs write permissions, and this appliance never attempts a write.

6. Run your first cloud assessment

Once an identity is attached, a full assessment is one command. Restrict it while you are getting oriented, then widen it:

# everything, written to a directory of your choosing
prowler azure --managed-identity-auth -o /var/lib/cloudimg/prowler/reports/first-run

# only one service
prowler azure --managed-identity-auth --service storage

# only critical and high severity findings
prowler azure --managed-identity-auth --severity critical high

# only what failed
prowler azure --managed-identity-auth --status FAIL

Prowler writes CSV, JSON in OCSF format, and a self contained HTML report. Copy the HTML report to your workstation to read it:

scp azureuser@<vm-ip>:/var/lib/cloudimg/prowler/reports/first-run/*.html .

7. Checks and compliance frameworks

You can enumerate everything Prowler knows about without any credentials at all, which makes this a good way to scope work before you connect an account.

prowler azure --list-checks --no-banner --no-color | tail -3
prowler aws --list-checks --no-banner --no-color | tail -3

At the version on this image that reports 191 checks for Azure and 621 for AWS, with a further 109 for Google Cloud and 92 for Kubernetes.

List the compliance frameworks available for a provider:

prowler azure --list-compliance --no-banner --no-color | grep -E '^ ?- ' | head -14

Expected output:

- c5_azure
- ccc_azure
- cis_2.0_azure
- cis_2.1_azure
- cis_3.0_azure
- cis_4.0_azure
- cis_5.0_azure
- cis_6.0_azure
- cis_controls_8.1
- csa_ccm_4.0
- dora_2022_2554
- ens_rd2022_azure
- fedramp_20x_ksi_low_azure
- hipaa_azure

To assess against one specific framework, name it:

prowler azure --managed-identity-auth --compliance cis_3.0_azure

Prowler check counts across the Azure, AWS, Google Cloud and Kubernetes providers, and the compliance frameworks available for Azure

8. Scheduled assessments

The appliance ships a systemd timer that runs an assessment daily, with a randomised delay so a fleet does not stampede.

systemctl status prowler-scan.timer --no-pager
cat /etc/cloudimg/prowler-scan.conf

The timer defaults to the credential free iac provider pointed at /opt/prowler/scan-targets, which ships empty. That is deliberate: the image makes no assumption about your estate, so out of the box the scheduled scan is a no operation rather than an error. It starts doing useful work as soon as you either:

  • drop Terraform, CloudFormation or Kubernetes manifests into /opt/prowler/scan-targets, or
  • point PROWLER_PROVIDER at a cloud provider once you have attached an identity, per section 5.

To switch it to a daily Azure assessment, edit the configuration:

sudo nano /etc/cloudimg/prowler-scan.conf

# set:
#   PROWLER_PROVIDER="azure"
#   PROWLER_ARGS="--managed-identity-auth"

sudo systemctl start prowler-scan.service

Reports are written to /var/lib/cloudimg/prowler/reports/, one dated directory per run, with the fourteen most recent kept.

The scheduled scan is fail closed, in two independent ways. For a security assessment tool the worst outcome is not "it did not run", which is obvious. It is a confident wrong answer: a clean looking report produced by a broken or tampered scanner, which you then file as evidence. So:

  1. The scan unit Requires= the first boot unit, so a failed first boot stops the schedule entirely rather than scanning against a half configured appliance.
  2. A preflight check re runs before every scheduled scan, not just the first, and verifies that the Prowler main module still matches the SHA-256 recorded when the image was built.

You can run that preflight yourself:

sudo /usr/local/sbin/prowler-preflight

It prints an OK line with the version it verified. If the scanner had been modified, it would refuse, and the scheduled scan would not run.

The scheduled assessment timer, its credential free default configuration, and the fail closed preflight verifying scanner integrity

9. Keeping the image current

The operating system and trivy patch themselves through Ubuntu's unattended upgrades. trivy ships from a third party repository, which stock unattended upgrades does not cover, so this image adds the vendor origin explicitly at /etc/apt/apt.conf.d/51cloudimg-unattended-trivy. That was proven at build time rather than assumed, by checking the origins the upgrade daemon actually loads.

Prowler itself is pip managed inside a virtual environment at /opt/prowler/venv, and is therefore not covered by unattended upgrades. That is stated plainly rather than glossed over. Upgrade it deliberately when you choose to:

sudo /opt/prowler/venv/bin/python -m pip install --upgrade prowler

One important caveat if you do that. This image deliberately ships cryptography 48.0.1 rather than the 46.0.7 that Prowler pins, because every cryptography wheel up to and including 48.0.0 statically links an OpenSSL affected by advisory GHSA-537c-gmf6-5ccf (high severity, CVSS 7.5). There is no fix in the 46.x series. cloudimg verified at build time that every Prowler provider still imports and that assessments still produce correct output with the newer library.

Upgrading Prowler with the command above will pull the vulnerable 46.0.7 back in. If you upgrade, re apply the fix:

sudo /opt/prowler/venv/bin/python -m pip install 'cryptography>=48.0.1'

The full explanation is on the image:

cat /usr/share/doc/cloudimg/PROWLER-DEPENDENCY-DEVIATION.txt

10. Scanning scope and authorisation

Prowler reads cloud provider APIs using the identity you give it. Two things follow, and both matter.

Only assess accounts you are authorised to assess. Point Prowler at your own subscriptions, projects and accounts. Running an assessment against infrastructure you do not own or administer may breach your cloud provider's acceptable use policy and, depending on where you are, the law.

Findings are sensitive. A Prowler report is a precise, prioritised list of the weaknesses in your cloud estate. Treat the report directory as confidential material: restrict access to the virtual machine, and be careful where you copy reports to.

11. Logs and troubleshooting

systemctl status prowler-firstboot.service --no-pager
journalctl -u prowler-scan.service --no-pager -n 20
ls -la /var/lib/cloudimg/prowler/reports/

A few things that commonly look like faults but are not:

  • Exit code 3 from a scan. That is a successful scan that found failing checks. See section 4.
  • pip check reports unsatisfied requirements. Expected, and explained in section 9. It reflects the deliberate cryptography upgrade, not a broken install.
  • The scheduled scan logs "nothing to scan". Expected until you populate /opt/prowler/scan-targets or switch provider. See section 8.
  • An Azure assessment reports permission errors on some checks. The identity is missing a role. Reader plus Security Reader covers the large majority; some identity and directory checks need Microsoft Graph permissions granted separately.

12. Licence

Prowler is licensed under the Apache License 2.0. cloudimg verified this against the upstream LICENSE artifact rather than a badge, and specifically confirmed that the project is not open core: the API and web interface directories in the Prowler repository carry no separate licence and inherit the same Apache 2.0 terms, so the self hosted edition is not a reduced version of a paid product. The licence text is on the image:

cat /usr/share/doc/cloudimg/PROWLER-APACHE-2.0-LICENSE.txt | head -5

trivy is licensed under the Apache License 2.0 by Aqua Security.

A vulnerability report for this image, generated during the build, is at /usr/share/doc/cloudimg/trivy-rootfs-report.json.

Prowler is a trademark of its respective owners. cloudimg is not affiliated with, endorsed by, or sponsored by the Prowler project or Aqua Security.

Support

This image is supported by cloudimg. If something here does not behave as described, contact support@cloudimg.co.uk.