Skip to content

DRY your configuration files with BigConfig

Stop writing configuration files and start writing infrastructure programs! If HCL or JSON/YAML is feeling like a restrictive intermediate language, BigConfig offers the escape hatch you've been searching for. Give it a try and experience a more productive, flexible, and powerful IaC workflow.

Configuration as code

We realized that designing a CLI for automation is an anti-pattern. The core principle is simple: code is a more effective and flexible tool than configuration languages. Instead of creating a new CLI tool to improve productivity, we chose to leverage something that already works exceptionally well: a dynamic programming language like Clojure.

Powered by Clojure

With Clojure, you already have the ability to handle any automation task. This is a contrast to many modern CLI tools, like Helm, Helmfile, Kustomize, CUE, Dhall, Jsonnet, KCL, and Pkl. BigConfig, implemented in Clojure, provides the essential building blocks — a workflow and a template engine — that let you use the full power of a dynamic programming language without compromise.

Flow control

Use Clojure instead of HCL. You should minimize the dynamic features of HCL that you use and use Clojure language features instead. count is an example of dynamic feature that you should avoid. The for expression in Clojure will achieve the same result.

Refactoring is trivial

The sort-nested-map function ensures that the resulting JSON output is consistent. This allows you to commit the main.tf.json file to Git, just as you would with a main.tf file. Committing this file provides a safety net: if you refactor your Clojure code, Git will flag any unexpected changes in the main.tf.json file, giving you confidence in your refactoring process.

You can convert your HCP files to EDN.

Terminal window
cat main.tf | hcl2json | jet --from json --to edn --pretty --keywordize

Eventually you can DRY your resources with functions.

(ns core
(:require
[big-config.utils :refer [deep-merge sort-nested-map]]
[big-tofu.core :refer [add-suffix construct]]
[big-tofu.create :as create]
[cheshire.core :as json]))
(let [{:keys [aws-account-id
region] :as data} {:aws-account-id "111111111111"
:region "eu-west-1"
:module "example"}
queues (->> (for [n (range 2)]
(create/sqs (add-suffix :alpha/big-sqs (str "-" n))))
flatten
(map construct))
kms (->> (create/kms :alpha/big-kms)
(map construct))
bucket (format "tf-state-%s-%s" aws-account-id region)
provider (create/provider (assoc data :bucket bucket))
m (->> [provider]
(into kms)
(into queues)
(apply deep-merge)
sort-nested-map)]
(json/generate-string m {:pretty true}))

A dynamic inventory written in Clojure can save a lot of time.

(defn inventory
[{:keys [sudoer hosts users]}]
(let [users (-> (filter (complement :remove) users)
(->> (map #(for [host hosts]
(assoc % :host host))))
flatten)
admins (-> [{:ansible_user sudoer}]
(->> (map #(for [host hosts]
(-> %
(merge {:host host
:name "ubuntu"})))))
flatten)
users-hosts (reduce #(let [{:keys [name uid host]} %2]
(assoc %1 (format "%s@%s" name host) {:ansible_host host
:ansible_user name
:uid uid})) {} users)
admins-hosts (reduce #(let [{:keys [name host]} %2]
(assoc %1 (format "root@%s" host) {:ansible_host host
:ansible_user name})) {} admins)
res {:all {:children {:admin {:hosts admins-hosts}
:users {:hosts users-hosts}}}}]
(generate-string res {:pretty true})))