CLI tool
SpecterOps/AzureHound avatar
SpecterOps/AzureHound

AzureHound is the BloodHound collector for Microsoft Azure

Azure Data Exporter for BloodHound

978 stars150 forksGoGPL-3.0

At a glance

What is it?
AzureHound enumerates objects and relationships in a Microsoft Azure tenant and hands them to BloodHound, either as a JSON file for the Community Edition or as a running collection service for the Enterprise edition. It reads through the Graph API with credentials you supply and changes nothing in the tenant.
Who is it for?
AzureHound suits identity security engineers who need the Azure side of an attack path mapped rather than inferred, and it suits BloodHound Community Edition users who want the same collector behaviour the Enterprise product uses.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A collector that answers one question about a tenant

BloodHound works on a graph of objects and the relationships between them. Its Azure collector fills in the Microsoft Azure side of that graph, and AzureHound is that collector.

The scope is narrow and that is the point. AzureHound does one job: it queries an Azure tenant and produces the objects and relationships BloodHound needs. The command help describes it as the official tool for collecting Azure data for BloodHound Community Edition and BloodHound Enterprise, and the project describes itself as the BloodHound data collector for Microsoft Azure.

Two audiences follow from that. The first is a team doing identity security work in an environment they are authorised to assess, who want the Azure relationships in the same graph format as the rest of their BloodHound data instead of correlating by hand. The second is a BloodHound Enterprise customer who needs a running collector service that feeds the platform rather than a file someone imports once.

It is worth stating the boundary plainly, because tools in this category get misread. AzureHound reads the tenant through the Graph API using credentials or a token you provide. It collects. It does not modify roles, create objects, or move anything. Any risk it carries is the risk of running an enumeration tool with a privileged account, not the risk of the tool acting on its own.

The project is also the first party to its own graph. The help output states that it was created by the BloodHound Enterprise team at SpecterOps, so the collector and the platform it feeds are made by the same organisation.

Three ways to authenticate, and what each one costs you

The quickstart offers three authentication paths, and the difference between them matters more in practice than the flags suggest.

The simplest path passes a username, a password and a tenant id, and prints the tenant data to stdout:

sh
azurehound list -u "$USERNAME" -p "$PASSWORD" -t "$TENANT"

Add an output path and the same data is written to a file instead, which is the form you want when the data is going into BloodHound Community Edition:

sh
azurehound list -u "$USERNAME" -p "$PASSWORD" -t "$TENANT" -o "mytenant.json"

The third path reuses a session you already have. The documentation shows pulling an access token from the Azure CLI and handing it to the collector:

sh
JWT=$(az account get-access-token --resource https://graph.microsoft.com | jq -r .accessToken)
azurehound list --jwt "$JWT"

There is also a refresh token flag, -r, --refresh-token, described as using an acquired refresh token to authenticate into Azure, and a separate --jwt flag for an acquired JWT.

The choice between them is about what the collection can see and what happens to the credentials. A token you already hold from the Azure CLI carries the scopes that CLI session was granted. A refresh token can outlive the command that issued it. A password in a shell argument is visible to anything reading the process list. None of this is a flaw in the tool, it is a set of options, but a reader should pick the path that matches how their organisation manages Graph access rather than reaching for the first example.

What every path shares is the constraint that matters most. The collector can only see what the identity behind it is allowed to see.

list output versus a collection service

The tool exposes two fundamentally different operating modes, and the choice between them is a decision about how AzureHound fits into your workflow.

The list command is a one-shot collection. It runs, queries the tenant, writes what you asked for, and exits. The output is a JSON file that you then import into BloodHound Community Edition yourself.

The service mode is for BloodHound Enterprise. The documentation describes configuring and starting a data collection service for BloodHound Enterprise:

sh
azurehound configure
(follow prompts)

azurehound start

The configure step is interactive, with prompts, and it writes a configuration file. The default location is shown in the flag help as /Users/dlees/.config/azurehound/config.json, and it can be pointed elsewhere with the -c, --config flag. The service mode also brings a --proxy flag, which sets the proxy URL for the AzureHound service, a piece of configuration that has no meaning in a one-shot run.

That difference is the real design decision. If you are on BloodHound Community Edition, the list command is the whole tool and the service mode is irrelevant to you. If you are on BloodHound Enterprise, you want the service, because the point of that edition is continuous collection rather than a one-off import you did last Tuesday.

One practical warning applies to both modes, and it comes straight from the flag documentation: only one AzureHound process should write to a given log file. Two collectors pointed at the same path will fight over it.

Building from source, and the container image

Release binaries are published per platform on the Releases page, so compiling is optional. When you do need to build, the prerequisite is Go 1.25 or later, and the build command embeds the version string at link time:

sh
go build -o azurehound -ldflags="-s -w -X github.com/SpecterOps/AzureHound/v2/constants.Version=`git describe tags --exact-match 2> /dev/null || git rev-parse HEAD`"

The version stamping has a fallback built in. It tries the exact git tag first, and falls back to the commit hash, which means a build from a checkout without the tag still reports something meaningful.

There is a version discrepancy worth knowing about. The README lists Go 1.25 or later as the prerequisite, while the go.mod file in the repository declares go 1.27.1, and the Dockerfile builds from golang:1.27.1. If you follow the README's stated minimum and hit a toolchain complaint, the go.mod requirement is the stricter of the two.

The container image is a two stage build. The first stage compiles with the version appended by a docker suffix, and the second stage is a distroless Debian 12 base running as nonroot, with the binary copied to the root and set as the entry point. The image therefore ships no shell and no package manager, which is a sensible way to distribute an enumeration tool that should be running with the least possible surface.

The dependency list in go.mod shows what the tool is made of: the Azure SDK for Go with azcore and azidentity, the Microsoft Authentication Library for Go, cobra for the command line, viper for configuration, zerolog and lumberjack for logging, promptui for the interactive prompts, and youmark/pkcs8 for certificate handling.

Log rotation rules you can tune

AzureHound has a log management section in its documentation, which is unusual for a tool of this kind and useful, because a collector that runs on a schedule will otherwise fill a disk.

The behaviour is driven by four flags. When --log-file is configured, AzureHound rotates the active log when it reaches --log-max-size, which defaults to 100 MiB. Rotated logs are timestamped, stored beside the active log, and compressed with gzip by default, which --log-compress turns off if you need the plain files.

Retention is bounded by age and by count. Archives are kept for at most --log-max-age days, defaulting to 14, and are also limited by --log-max-backups, defaulting to 20. Setting either retention option to 0 disables that individual limit, which is worth remembering in both directions: it is how you keep everything, and it is how you keep nothing but the active file.

The output format is switchable too, with --json producing logs as json for ingestion into a log pipeline, and -v, --verbosity taking a level from minus one to two, defaulting to zero.

For a defender, that combination matters. A collector running against a production tenant on a schedule produces a log that describes who read what in the directory, and a log format that can be shipped to a SIEM is the difference between evidence you can use later and a text file on a jump host.

The rolling release trades stability for freshness

Two release channels exist, and the difference between them is a deliberate trade-off rather than an accident.

The tagged releases are the stable ones, with v3.1.1 published on 2026-09-08, v3.1.2 on 2026-10-07 and v3.1.3 on 2026-10-16. The last push to the repository was on 2026-10-16, and the repository is not archived.

The rolling release is the other channel. The documentation describes it as containing pre-built binaries that are automatically kept up to date with the main branch, downloadable from a rolling tag. The warning attached to it is that the rolling release may be unstable.

That is the right design for this class of tool. A collector that only runs occasionally can wait for a tagged build, while a team running continuous collection against BloodHound Enterprise may prefer a build that already contains the fixes for a Graph API change. The trade is that an unstable build can fail in the middle of a collection window, and a failed collection means a gap in your graph.

If you are evaluating the tool, pick the tagged release. If you are running it in production on a schedule, the rolling channel exists precisely so that you are not waiting weeks for a Graph API change to be packaged, and you should decide how you detect a bad build before you rely on it.

The licence is GPL-3.0, with the licence text in LICENSE.md and attribution details in NOTICE.md. For internal security work that is usually straightforward, and anyone redistributing the binary as part of a product should read those two files rather than assume the terms of the surrounding BloodHound platform apply.

Where AzureHound falls short as a tool

The honest limitations come from what the collector is and is not.

First, it is read only and it is scoped by your token. There is no way to make it see objects that the calling identity cannot see through Graph. In a tenant where directory roles have been tightened, the resulting graph is thinner, and it is thinner in a way that looks like an absence of attack paths rather than an absence of permissions. That is a real trap for anyone reading the output as a complete picture of the tenant.

Second, the output is BloodHound data, which means it is shaped for BloodHound. If you want to answer a question about Azure that is not a question about attack paths, you are parsing a BloodHound JSON file with a general purpose tool, and the documentation points you to the BloodHound Community Edition documentation rather than to a schema for the raw tenant data.

Third, the documentation is split across two places. The README covers getting the binary, compiling, the quickstart and the flags, and then sends you to the BloodHound Community Edition documentation for AzureHound Community Edition and for the flags page. There is no single page in this repository that ties the two together, which makes the first session with the tool more piecemeal than it needs to be.

Then there is the alternative worth naming. AzureHound depends on the Azure SDK for Go, with azcore and azidentity in the dependency list, so a team that needs a small number of specific objects can write that collection directly against the SDK using the same credential types and skip the BloodHound output format altogether. The difference in approach is who owns the graph: with AzureHound the shape of the data is decided for you, and the tool is short enough that a bespoke collector is a realistic option. The cost of going that way is that you have to build, maintain and update the queries yourself as the directory changes, and you lose the continuous collection service that the Enterprise mode gives you.

Finally, there is the version drift already noted, and a documentation gap around the service mode. The README shows configure and start with a follow prompts note, and the flag help shows the default configuration path, but the details of what the configuration prompts actually collect, and what the service does with them, are in the BloodHound Enterprise documentation rather than here.

Editorial conclusion

AzureHound suits identity security engineers who need the Azure side of an attack path mapped rather than inferred, and it suits BloodHound Community Edition users who want the same collector behaviour the Enterprise product uses. It does not suit anyone hoping to change or harden the tenant, since collection is read only, and it is the wrong tool if you need the Graph permissions or the token scope it needs, since the collection surface is exactly the permissions your account already holds. Before running it, confirm your account has the Graph read scopes the collection requires, decide between the list command and the collection service, and read the log retention flags before pointing --log-file at a path that matters.

Frequently asked questions

What does AzureHound collect from an Azure tenant?

It is the BloodHound data collector for Microsoft Azure, and the list command prints all Azure tenant data to stdout or to a JSON file such as mytenant.json. The data is shaped for BloodHound, and the tool reads the tenant through the Graph API without modifying it.

How do I authenticate AzureHound without typing a password?

You can pass an acquired token instead. The documentation shows obtaining a JWT from the Azure CLI with az account get-access-token --resource https://graph.microsoft.com and then running azurehound list --jwt with that value, and there is also a --refresh-token flag for an acquired refresh token.

What is the difference between the list command and the AzureHound service?

The list command is a one-shot collection that writes tenant data to stdout or a file for import into BloodHound Community Edition. The configure and start commands run a data collection service for BloodHound Enterprise, with a configuration file and a --proxy option for the service URL.

Which Go version do I need to compile AzureHound?

The README lists Go 1.25 or later as the prerequisite for the build command that embeds the version through ldflags. The repository go.mod declares go 1.27.1 and the Dockerfile builds from golang:1.27.1, so that is the stricter requirement in practice.

How long does AzureHound keep its logs?

With --log-file set, the active log rotates at --log-max-size, 100 MiB by default, and rotated logs are compressed with gzip unless --log-compress is turned off. Archives are retained for at most --log-max-age days, 14 by default, and at most --log-max-backups files, 20 by default, and setting either to 0 disables that limit.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. README
  4. Releases
  5. SpecterOps/AzureHound on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/specterops-azurehound.svg)](https://hysenlabs.com/projects/specterops-azurehound)

Was this useful?