Mike Dalrymple Declarative Config

Declarative Config

I get confused with the difference between imperative and declarative configuration. This post is a quick reminder for myself with a simple Terraform example.

Summary

Declarative configuration is the one we like the most because we declare what we want the result of our configuration to be, and we leave it to the tooling to get us there. An imperative configuration requires us to provide all the steps necessary to create our desired configuration (much like a recipe) and it’s up to us to keep track of the state/result of the configuration.

S3 Bucket Example

The Terraform and AWS CLI examples here create a trivial S3 bucket configuration for use as a static website. These examples assume the tooling has been configured with the keys and permissions required to create the resources.

Declarative - Terraform

provider aws {
    region = "us-east-2"
}
resource aws_s3_bucket website {
    bucket = "web.example.com"
    acl = "public-read"
}
resource aws_s3_bucket_website_configuration {
    bucket = aws_s3_bucket.website.bucket

    index_document {
        suffix = "index.html"
    }

    error_document {
        key = "error.html"
    }
}

Running terraform apply with this configuration should result in the same configuration every time. The first time it will create the necessary resources and after that (assuming no changes), it will leave everything alone and exit without errors.

Imperative - AWS CLI

Using the AWS CLI, we call the create-bucket and put-bucket-website commands. The put-bucket-website command requires the following JSON file as input.

website.json

{
    "IndexDocument": {
        "Suffix": "index.html"
    },
    "ErrorDocument": {
        "Key": "error.html"
    }
}
aws s3api create-bucket \
    --bucket web.example.com \
    --region us-east-2
aws s3api put-bucket-website \
    --bucket web.example.com \
    --website-configuration file://website.json \
    --regions us-east-2

These CLI commands can only be run once because they have not concept of state. If we run them again, we’ll get an error stating that the bucket name is taken. This becomes brittle as the infrastructure evolves, and you need to make changes and don’t want to start from scratch.

Conclusion

Declarative configuration relies on the tools to understand the current state of the system, and how to get from the current state to the desired state. We really need to trust these tools (ideally by reviewing their source) because they’re doing a lot of heavy lifting for us and require root-like permissions.