This guide will walk you through configuring .resourcely.yaml in your Terraform repository.
What is .resourcely.yaml?
.resourcely.yaml is a config file that provides Resourcely with metadata about the structure of a repository's Terraform:
paths to the Config Roots in the Repo. These are where Resourcely can emit Terraform.
for config roots that are instantiated in multiple environments, what these environments are and where the environment-specific values (*.tfvars) are stored.
Resourcely Create will use this metadata to help developers
place their Terraform config in the correct paths
provide values for all environments
without them needing to be aware of the repository or Terraform structure themselves.
Where to place .resourcely.yaml?
The .resourcely.yaml files may be placed anywhere within a repo. Multiple such files are allowed, primarily to support large mono-repos where subdirectories are owned by different teams.
The only restriction is that .resourcely.yaml files cannot be nested. No subdirectories below a .resourcely.yaml may contain a .resourcely.yaml.
.resourcely.yaml Structure
There are a variety of ways that Terraform config can be structured, including:
No-envs: One config root, no environments, one state file.
Multiple-envs: One config root, one or more *.tfvars files per environment, one state file per environment.
Module: One config root that contains reusable Terraform code to be invoked by other config roots. (coming soon)
Resourcely automatically detects which config roots are modules. The contents of .resourcely.yaml specify whether a config root has environments or not.
version: "2"
# Each .resourcely.yaml file manages all config roots in or below its directory.
#
# - A subdirectory of a .resourcely.yaml directory may not contain its own
# .resourcely.yaml file.
#
# - All the paths and files referenced in a .resourcely.yaml must be in or below
# its directory. References cannot "escape" that directory.
# List of directories that contain Terraform configuration.
#
# In the Resourcely Pull Request UI, a developer will pick one of these
# locations to put their new resources in.
terraform_config_roots:
- # Name that will be shown in the Resourcely Pull Request UI
# dropdown menu.
#
# Example: "Live Site Infra"
name: <string>
# (optional)
#
# Longer description for the config root. If developers may have trouble
# selecting the correct config root, use this text to help guide them.
description: <string>
# (optional)
#
# Instructs Resourcely to ignore this config root. It will not be
# shown in the Resourcely Pull Request UI dropdown menu.
skip: true | false
# Path to the config root, the directory containing the *.tf
# files.
#
# Relative to the location of this .resourcely.yaml file
#
# If the config root is the same directory, specify
# path: .
#
# Example: live_site
path: <string>
# (optional, conflicts with option_groups)
#
# Name of the file in the `path` directory in which to place
# new resources by default.
#
# Developers may pick a different file, but will be defaulted to this one.
#
# Example: main.tf
default_file: <string>
# (optional, conflicts with option_groups)
#
# Name of the file in the `path` directory in which to place new
# variable declarations for new environment-specific values.
#
# For `module` configs, these variables are the module's inputs.
#
# Example: variables.tf
var_file: <string>
# (optional, conflicts with default_file and var_file)
#
# Option group allow you to specify different conventions for producing
# Terraform into this config root's directory. When creating a new pull
# request, users will choose an option group, rather than the "base"
# config root.
#
# Use option_groups when a single directory has multiple conventions,
# such as placing new resources in "frontend.tf" vs "backend.tf".
#
# If option_groups is specified, it is illegal to specify default_file
# or var_file. If either default_file or var_file is specified, it is
# illegal to specify option_groups.
option_groups:
- # (required, must be unique within the config_root)
#
# A non-user-visible identifier for the option group. Change the id
# causes unwanted behavior when users edit old pull requests through
# Resourcely.
#
# Example: 'frontend' or 'backend'
id: <string>
# (required, must be unique within the config_root)
#
# The user-visible label for this option group. Changing the label
# is safe.
#
# Example: 'Frontend' or 'Backend'
#
# Using the example from config_root.name and the two examples above,
# users would see:
# - Live Site Infra (Frontend)
# - Live Site Infra (Backend)
label: <string>
# (optional)
#
# Name of the file in the `path` directory in which to place
# new resources by default. Similar to config_root.default_file.
#
# Example: frontend.tf or backend.tf
default_file: <string>
# (optional)
#
# Name of the file in the `path` directory in which to place new
# variable declarations for new environment-specific values. Similar
# to config_root.var_file.
#
# Example: frontend_variables.tf, backend_variables.tf
var_file: <string>
# (optional)
#
# Name of the file in which to place the variables definitions
# for this config root.
#
# Mutually exclusive with the `environments` option below. Use this if
# your config root does not have multiple environments, but does use
# tfvars as way of defining "constant" values.
#
# The config root name will be used for `context.environment` in
# guardrails and blueprints.
#
# Relative to the config root `path` directory.
#
# Example: constants.tfvars
tfvars_file: <string>
# (optional)
#
# Additional files that contain existing variable declariations.
# If the same variable appears in more than one of these files (or in tfvars_file and one of these files),
# Resourcely will use the value in tfvars_file or the one that comes last in this list.
#
# Allowed, but not required, when tfvars_file is set.
#
# Relative to the config root `path` directory.
#
# Example: ../global.tfvars
extra_tfvars_files: <list of strings>
# (optional)
#
# Context from the config root that will be used when evaluating context-aware guardrails, such as
# `WHEN aws_s3_bucket AND CONTEXT data = "PII"`.
# The key of the map might match an existing Global or Local context question.
# If there is a conflict between a map key and a context question, Resourcely will use the value from the config root.
# The value of the map entry should be one of the options for a single/multi-select question
# or match the format for a text question.
# Example:
# context:
# data: "PII"
# owner: "example@resourcely.io"
context: <map of strings>
# (optional)
#
# List of the environments that this config root supports.
#
# Mutually exclusive with the `tfvars_file` option above. Use this if
# you run `terraform apply` in this config root multiple times with
# different values for the vars.
#
# If this list is missing or empty and `tfvars_file` above is not set,
# developers will not be allowed to use Terraform variables.
#
# If non-empty, a developers will be allowed to use Terraform variables.
# For any new variable, they will have to supply a value for each
# environment in this list.
environments:
- # Name of the environment.
#
# This name is used for two purposes.
# 1. It is shown in the UI to the developers.
# 2. It is used as the value for `context.environment` in guardrails
# and blueprints.
name: <string>
# Name of the file in which to place the variables definitions
# for this environment.
#
# Relative to the config root `path` directory.
#
# Example: envs/dev.tfvars
tfvars_file: <string>
# (optional)
#
# Additional files that contain existing variable declariations.
# If the same variable appears in more than one of these files (or in tfvars_file and one of these files),
# Resourcely will use the value in tfvars_file or the one that comes last in this list.
#
# Allowed, but not required when tfvars_file is set.
#
# Relative to the config root `path` directory.
#
# Example: ../global.tfvars
extra_tfvars_files: <list of strings>
# (optional)
#
# Context from this environment that will be used when evaluating guardrails.
# For example, `WHEN aws_s3_bucket AND CONTEXT tier = "1"`
# The key of the map might match an existing Global or Local context question.
# If there is a conflict between a map key and a context question or a config-root-level value, Resourcely will use the value from the environment.
# The value of the map entry should be one of the options for a single/multi-select question
# or match the format for a text question.
#
# These answers do not need to be defined for every environment.
# Example:
# context:
# tier: "1"
context: <map of strings>
Examples
# Repo with one very simple config root
#
# Assume the repo is stuctured like
#
# network.tf
# main.tf
# providers.tf
# terraform.tf
version: "2"
terraform_config_roots:
- name: "Project A"
path: .
default_file: main.tf
# Repo with one config root that uses tfvars
# to hold constant values, but not for multiple environments.
#
# Assume the repo is stuctured like
#
# network.tf
# main.tf
# providers.tf
# terraform.tf
# variables.tf
# constants.tfvars
version: "2"
terraform_config_roots:
- name: "Project B"
path: .
default_file: main.tf
var_files: variables.tf
tfvars_file: constants.tfvars
# Repo with one Terraform root module that uses tfvars
# to instantiante in multiple environments.
#
# Assume the repo is stuctured like
#
# network.tf
# main.tf
# providers.tf
# terraform.tf
# variables.tf
# global.tfvars
# envs/
# | dev.tfvars
# | stage.tfvars
# | envs/prod.tfvars
version: "2"
terraform_config_roots:
- name: "Project C"
path: .
default_file: main.tf
var_file: variables.tf
environments:
- name: dev
tfvars_file: envs/dev.tfvars
extra_tfvars_files: ["global.tfvars"]
- name: stage
tfvars_file: envs/stage.tfvars
extra_tfvars_files: ["global.tfvars"]
- name: prod
tfvars_file: envs/prod.tfvars
extra_tfvars_files: ["global.tfvars"]
You can use the scaffolding we created to get started with the basic resourcely.yaml file.
If you have added the Resourcely.yaml file and still cannot see the config root in the pull request creation form, you need to trigger a fresh scan. To do this, click on Settings -> Update Settings -> Queue Scan, and wait 1-2 minutes for the scan to detect the new Resourcely.yaml file.
Context in resourcely.yaml
If you want guardrails to behave differently in different config roots or environments, you can specify metadata in .resourcely.yaml. Guardrails can access this metadata with syntax like this:
GUARDRAIL "Production buckets must have suffix -prod"
WHEN aws_s3_bucket AND CONTEXT production = "true"
REQUIRE bucket ENDS WITH "-prod"
.resourcely.yaml can provide metadata for each config root or environment. Guardrails will load the correct metadata based on resourcely-cli arguments - see Advanced resourcely-cli usage.
With this yaml, guardrails would use the following metadata, depending on which environment they are evaluating:
// When evaluating plans for 'dev':
tier = "0"
production = "false"
environment = "dev"
// When evaluating plans for 'prod':
tier = "0"
production = "true"
environment = "prod"
Resourcely automatically provides some metadata out of the box:
environment is the name of the environment being evaluated.
When running resourcely-cli in your pipeline, please ensure you include the --config_root_path and --environment options. This allows the CLI to properly load the context from the specified configuration root and environment.
.resourcely.yaml can specify multiple values for the same key. It can also specify values for keys that match Global Context questions. When a collision occurs, Resourcely picks a value based on the following rules:
Values from environment.context in .resourcely.yaml have the highest priority.
Values from terraform_config_roots.context in .resourcely.yaml have the next-highest priority.
Values collected from the user through Global Context questions have the lowest priority.
Creating resourcely.yaml via the API
The following endpoint sets the Terraform configuration for a repo used with Resourcely. It can be used in conjunction with .resourcely.yaml files. If a config root is present in both an API config and a .resourcely.yaml config, the API config will take precedence.
Each POST request must include the configurations for ALL config roots that are to be set via the API. For example, if configuration roots foo, bar, and baz are set via the API in one call, and a subsequent call specifies only foo and bar, the configuration for baz will no longer be managed through the API.
Endpoint
POST /api/v1/terraform/config
Request
repo_url (string, required): Git repository URL. Resourcely will scan this repo asynchronously to determine existing resources within config roots
version (string, required): Version of config structure
The following endpoint gets the Terraform configuration for a repo used with Resourcely. It can be used to get configurations set with .resourcely.yaml files as well those set by API.
Endpoint
GET /api/v1/terraform/config
Query Params
repo_url (string, required): Git repository URL.
source (string, optional): If provided, filters Terraform configs to those set by API or by scanning .resourcely.yaml.