CLI tool
apenella/go-ansible avatar
apenella/go-ansible

go-ansible: driving the Ansible CLI from a Go program

Go-ansible is a Go package that enables the execution of ansible-playbook or ansible commands directly from Golang applications. It supports a wide range of options for each command, enabling smooth integration of Ansible functionality into your projects.

1,123 stars145 forksGoMIT

At a glance

What is it?
go-ansible is a Go library that shells out to ansible-playbook, ansible, ansible-galaxy and ansible-inventory, giving you typed command structs, pluggable executors and structured output instead of hand-built argument lists. It does not reimplement Ansible, so Python and the CLI have to be on the machine, and it is aimed at Go services that need configuration management as part of a larger workflow.
Who is it for?
go-ansible fits a Go team that has to run Ansible as one step inside a larger application, that wants vault passwords and environment configuration handled in Go rather than in a shell script, and that parses structured results instead of scraping stdout. It does not fit a team hoping to drop the Python dependency, and it does not fit a project that only needs to run one playbook, where a plain exec call is less code.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 24 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 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

It wraps the CLI rather than reimplementing Ansible

go-ansible is a Go library for executing Ansible commands such as ansible-playbook, ansible-inventory, ansible-galaxy and ansible, and the design decision that shapes everything else is that it is a wrapper around the Ansible command line interface. The project states plainly that rather than reimplementing Ansible's logic, the library provides an abstraction layer over the existing commands, so the actual work of a playbook still happens in Ansible and in Python. The motivation given is concrete: managing complex playbooks that need a large number of extra variables, which is painful when every invocation is a hand-assembled string of flags. The project started as a wrapper around ansible-playbook only, tightly coupled to the Stevedore project, and says it became general purpose through community contributions, feedback and advice. That history explains the package layout, which is organised per command rather than around one generic runner.

Executor, command generator and results handler are the three concepts

The documentation names three abstractions, and they are the right way to understand the library. The Executor is what actually runs the command, with a DefaultExecute implementation provided and an interface you can implement yourself. The Command Generator is the piece that turns a command struct, such as AnsiblePlaybookCmd or AnsibleAdhocCmd, into the argument list for the CLI. The Results Handler takes what comes back and turns it into something a Go program can use, and the package layout shows how far this goes: there are separate result types for plain text, for JSON and for JSONL, plus a transform step between the raw output and the structured result. Around those sit smaller extension points, a Commander interface and a Cmder interface that separates building the command from running it, and an ErrorEnricher interface for adding context to failures. The point of the split is that you can replace any single layer without rewriting the rest.

Command structs replace flag soup

Each Ansible command has its own struct in its own package, and each of those has an options struct beside it. AnsiblePlaybookCmd is paired with options, AnsibleAdhocCmd with AnsibleAdhocOptions, AnsibleInventoryCmd with AnsibleInventoryOptions, and the Galaxy package has separate structs for collection installs and role installs. Options are passed as Go values rather than as command line strings, so the compiler catches a typo that would otherwise become a runtime flag error, and the struct fields document themselves when you read the package. The adhoc package is the one to look at if you want the shape of the API, because it covers the general case of running ad hoc commands with module arguments rather than a fixed playbook. The library also supports chaining several Ansible executions into a workflow, so roles or collections can be installed and then playbooks run, sequentially, within a single workflow object.

Vault handling is built in rather than left to you

Secrets are where a wrapper earns its keep, because the naive approach of a shell script means putting a password on a command line. The project lists Vault integration as a key feature, covering encrypted variables, password files and multiple password sources. That means the password can be supplied by a file or by more than one source rather than a single literal, and it means the playbook and the secret handling can live in the same Go program. The dependency list backs this up: sosedoff/ansible-vault-go is in the module requirements, so vault handling is a real dependency rather than a note in the documentation. Alongside it, the library supports customising Ansible environment variables and settings from Go code, and the configuration package exposes functions for setting Ansible configuration values, which is the answer to playbooks that insist on specific settings such as inventory paths or verbosity.

Output handling is where a Go program actually benefits

The result types are the concrete advantage over shelling out by hand. The library handles command output in plain text, JSON and JSONL, with built-in handlers and transformers, and there is a specific result type for JSONL events on stdout that is clearly aimed at long-running playbook output. The examples directory names the scenarios: a simple embedfs based run, a run with extra variables, a run with extra variables from a file, a run with a prompt, a run that skips failing hosts, a run using become, a run with SSH become root and a password, a run under the cobra command framework, a run with a custom transformer, a run with a custom executor, and a run handling signals and cancellation. The signals example matters more than it looks: killing a Go process that is supervising a long playbook is a different problem from interrupting a shell script, and having a documented way to do it is worth knowing about before you need it.

Running the toolchain needs Docker, and the compose file shows it

The project builds and tests inside containers rather than expecting a local Go installation, and the compose file makes the versions explicit. The first service runs the official Go image, with the version taken from an environment variable that defaults to 1.26, mounts the repository at /app, and by default prints the Go version. The second runs the golangci-lint image with its own version variable, which defaults to v1.57.2, and its entrypoint is the linter itself with a version check as the command:

yaml
  golang-ci:
    image: golang:${GOLANG_VERSION:-1.26}-alpine
    volumes:
      - .:/app
    working_dir: /app
    command: ["go", "version"]

The Makefile then wraps those two services in variables and exposes the usual targets, so the commands you type are short. Running the unit tests looks like this:

bash
	@$(GOLANG_BINARY) test ./pkg/... -cover -count=1

The count of 1 disables test caching, which is what you want in a container that is recreated on every run, and the coverage flag is on by default. The vet and lint targets follow the same pattern, and there is a list-examples target that simply lists the examples directory, which is a quick way to see the full set of supported scenarios.

Where go-ansible is the wrong tool

There are two cases where reaching for this library costs more than it saves. The first is a project that runs exactly one playbook with fixed arguments and no vault and no structured output. In that situation the whole thing is one exec call with a slice of arguments, and the library's package structure, options structs and result types are overhead you carry to gain nothing. The second is a deployment that has no Python and no Ansible installed, because the library is explicitly a wrapper and inherits that requirement wholesale. The documentation also has a section on executing go-ansible inside a container and a section on disabling pseudo-terminal allocation, which tells you the project is aware that the wrapper's behaviour depends on the environment it runs in. If your application is already a Go binary that has to talk to a configuration management system, this library is a reasonable way to keep one language in the stack, and the price is the Python toolchain and a subprocess boundary you have to reason about.

A v2 module path and a dependency list that includes Docker

The module path carries a major version suffix, github.com/apenella/go-ansible/v2, and the documentation has an upgrade notes section alongside the installation instructions, which is the signal that a path change happened and that older code will need its imports updated. The Go version required is 1.26.0, set in the module declaration, which is at the top of what a team has to satisfy. The dependency list is worth reading before you depend on it, because it is longer than a thin wrapper would suggest: it includes the Docker client and the moby API, which is how the library supports running a playbook inside a container, cobra for the command framework integration, goquery for parsing, fatih/color for terminal output, strcase for case conversion, and afero for the filesystem abstraction that the embedfs examples use. The MIT licence field on the repository and the MIT badge both say MIT, so reuse in commercial products is not a question you need to answer first. The last push was on 2026-09-30, and the release history shows v2.5.0 on the same day, v2.4.2 earlier in the month and v2.4.1 in November 2025.

Editorial conclusion

go-ansible fits a Go team that has to run Ansible as one step inside a larger application, that wants vault passwords and environment configuration handled in Go rather than in a shell script, and that parses structured results instead of scraping stdout. It does not fit a team hoping to drop the Python dependency, and it does not fit a project that only needs to run one playbook, where a plain exec call is less code. Before adopting it, check that ansible is installed where the process will run, read the upgrade notes in the documentation because the module path carries a major version suffix, and confirm your Go toolchain is at 1.26, the version the module declares.

Frequently asked questions

Does go-ansible require Ansible to be installed?

Yes. It is a wrapper around the Ansible command line interface rather than a reimplementation, so ansible-playbook, ansible, ansible-galaxy and ansible-inventory need to be available where the Go process runs. The documentation also covers running the library inside a container and disabling pseudo-terminal allocation.

How do I run a playbook with go-ansible?

You build an AnsiblePlaybookCmd struct with its options, run it through an executor such as DefaultExecute, and read the result through a results handler. The library takes care of turning the struct into the command line and of parsing plain text, JSON or JSONL output into structured results.

Can go-ansible handle Ansible Vault passwords?

Yes, vault integration is listed as a key feature, covering encrypted variables, password files and multiple password sources. The dependency list includes sosedoff/ansible-vault-go, so the support is implemented rather than documented as a future idea.

What Go version does go-ansible need?

The module declaration requires Go 1.26.0. The repository also builds and tests inside Docker, with a compose service running the golang image using a GOLANG_VERSION variable that defaults to 1.26.

Official sources

  1. apenella/go-ansible on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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/apenella-go-ansible.svg)](https://hysenlabs.com/projects/apenella-go-ansible)

Was this useful?