Writing a terraform provider for AWX
AWX has a perfectly good API, but managing it by hand stops scaling the moment more than one person touches it.
AWX has a perfectly good API, and for a while I was quite happy poking at it with curl and a few scripts.
That works until somebody else needs to change something, and then you find out that nobody knows what the current state is supposed to be.
So I wanted the job templates, the inventories, the credentials and the organizations to live in terraform like everything else does, instead of in somebody’s shell history.
There are providers for AWX already, but the ones I looked at cover the resources somebody needed at the time, and the AWX API is a lot bigger than that.
I kept running into a resource that wasn’t there, and adding it meant writing the whole thing by hand anyway, so the first version of this was exactly that, written by hand: organizations, credentials, job templates, inventories, and the associate and disassociate calls that AWX uses for relationships.
In AWX you don’t just set a field to attach a Galaxy credential to an organization.
We have to POST to a sub-endpoint, and POST again with a disassociate flag when we want it gone.
curl -sk -u admin:password \ https://awx.example.com/api/v2/organizations/1/galaxy_credentials/ \ -H 'Content-Type: application/json' \ -d '{"id": 4}'That is the kind of thing that is easy to do once and easy to forget, which is the whole argument for putting it in terraform.
The provider is built on the terraform plugin framework rather than the old SDK.
Every resource is plain Go with a schema, a create, a read, an update and a delete, nothing clever.
func (o *organizationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "name": schema.StringAttribute{Required: true}, "description": schema.StringAttribute{Optional: true, Computed: true}, }, }}The one thing I did get right early is that the provider sends its own version in the user agent, so when something goes wrong on the AWX side we can at least work out which build did it.
What I did not get right is that I was hand writing files the API already describes.
Every resource is a schema I typed out from reading the documentation, and if you send an OPTIONS request to the endpoint AWX will tell you the same thing itself.
I noticed that fairly early and then ignored it for about a year.
The code is on GitHub.