Configuration file format

A Sagan configuration file is a YAML file that contains helpers, workflows, and tasks.

A helper is a task that runs at the start and end of a workflow. This can be a script that manages a tunnel, fetches some credentials to be used as part of a workflow, or any number of other tasks.

A workflow is a list of commands that implements the various deployment phases of a task. In Terraform, this would be initialising a project, planning it, applying it, and cleanup.

A task is a directory full of configuration, typically a Terraform project, that a workflow describes how to manage.

Example

---
version: "1.0"

helpers:
  tunnel:
    # Helpers of type 'daemon' persist until they are no longer needed. The
    # duration of their persistence is decided by the values of the arguments
    # they expect. As long as there is at least one task that expects an
    # instance of the helper with a particular set of arguments, the helper
    # will keep running.
    type: daemon
    args:
      - name: cidr
        # This argument has a default value
        default: 192.168.0.0/16
        # This argument is used to prevent multiple instances of helper
        # running at once. This is useful for circumstances where you may,
        # for instances, reuse a private range in multiple environments and
        # want to avoid confusing sshuttle.
        exclusive: true
      - name: environment
      # The value of this argument is written to an environment variable
      - name: profile
        env: AWS_PROFILE
      - region: region
        # This argument also has a default
        default: us-east-1
        env: REGION
    # Only the last command is kept running in the backgroun until it exits.
    # By default, it will send a SIGTERM to the last command upon shutdown.
    run:
      - cmd: >
          aws ec2 describe-instances
            --filter "Name=tag:Env,Values=$environment" "Name=tag:Role,Values=bastion"
            --query "Reservations[].Instances[].InstanceId | [0]"
            --output text
        save_as: instance_id
      - cmd: aws ssm start-session --target $instance_id
      - cmd: >
          sshuttle
            --ssh-cmd="ssh -o ProxyCommand='aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters portNumber=22'"
            --remote ec2-user@$instance_id $cidr

  vault:
    # A one-shot helper that may prompt for input.
    type: interactive
    # Is there a helper that needs to be running before this helper can be
    # used?
    requires:
      - tunnel
    args:
      - name: environment
    run:
      - cmd: vault login -method=ldap -address=https://vault.$environment.infra.example.com -no-store
        save_as: VAULT_TOKEN
    # If this command will need to be re-executed after a while, how long
    # should the result be valid for?
    ttl: 2h

workflows:
  default:
    temporaries:
      - name: plan
        type: file
    load:
      # This is basically the default behaviour
      - "*.auto.tfvars.json"
      - "terraform.tfvars.json"
    init:
      run:
        - cmd: terraform init
      finalize:
        - cmd: rm -rf .terraform
    plan:
      requires:
        ".terraform": init
      run:
        - cmd: terraform plan -plan $plan
      finalize:
        - cmd: rm -rf $plan
    apply:
      requires:
        "$plan": plan
      run:
        - cmd: terraform apply $plan

tasks:
  # Every task has a path. The last element in the path is used as the
  # default task name.
  - path: fred
    # If you want to override the task name, you do it with 'name'
    name: frederick
    # The workflow to use.
    workflow: default
    helpers:
      # Will implicitly run the 'tunnel' helper too.
      - vault

  - path: barney
    workflow: default
    # When rolling out this set of tasks, 'frederick' must be deployed
    # before 'barney'
    requires:
      - frederick
    helpers:
      # This task doesn't require Vault, but it does require a tunnel.
      - tunnel
    # We want to save some of this task's outputs to a configuration
    # file for the 'bamm-bamm' task.
    outputs:
      - write: ../bamm-bamm/terraform.tfvars.json
        # Overwrite the value of the field. Other actions are 'add'.
        action: replace
        # The name of the field to overwrite.
        name: thingy

  - path: betty
    workflow: default

  - path: bamm-bamm
    requires:
      - betty
    # This creates an indirect dependency on 'barney': if the field 'thingy'
    # in the named file changes, this task should be redeployed.
    redeploy_on:
      - file: terraform.tfvars.json
        name: thingy