Resourcely.yaml
Configuring Resourcely.yaml
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: "[email protected]"
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"]
# Repo with multiple Terraform root modules.
#
# Assume the repo is stuctured like
#
# projectA/
# | network.tf
# | main.tf
# | providers.tf
# | terraform.tf
# projectB/
# | network.tf
# | main.tf
# | providers.tf
# | terraform.tf
# | variables.tf
# | envs/
# | | dev.tfvars
# | | stage.tfvars
# | | prod.tfvars
version: "2"
terraform_config_roots:
- name: "Project A"
path: projectA
default_file: main.tf
- name: "Project B"
path: projectB
default_file: main.tf
var_file: variables.tf
environments:
- name: dev
tfvars_file: envs/dev.tfvars
- name: stage
tfvars_file: envs/stage.tfvars
- name: prod
tfvars_file: envs/prod.tfvars
How to create resourcely.yaml
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.
You can specify metadata like this:
version: "2"
terraform_config_roots:
- name: "Project C"
path: .
context:
tier: "0"
production: "false"
environments:
- name: dev
tfvars_file: envs/dev.tfvars
- name: prod
tfvars_file: envs/prod.tfvars
context:
production: "true"
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"
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.
Example:
resourcely-cli --config_root_path {config_root_name} --environment {environment_name}
Collisions
.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
Supported values: ["1", "2"]
provider
(string, required): Git provider
Supported values: ["REMOTE_GIT_GITHUB", "REMOTE_GIT_GITLAB"]
terraform_configs
(list, required): List of Terraform configs. See .resourcely.yaml
documentation for field descriptions
An empty list will delete all existing Terraform configs set by the API. Configuration will fall back to .resourcely.yaml
files (if they exist).
Example
Assume a repo is structured as follows:
# <https://github.com/ResourcelyTest/terraform>
# projectA/
# | network.tf
# | main.tf
# | providers.tf
# | terraform.tf
# projectB/
# | network.tf
# | main.tf
# | providers.tf
# | terraform.tf
# | variables.tf
# | envs/
# | | dev.tfvars
# | | stage.tfvars
# | | prod.tfvars
{
"repo_url": "<https://github.com/ResourcelyTest/terraform>",
"version": "2",
"provider": "REMOTE_GIT_GITHUB",
"terraform_configs": [
{
"name": "Project A",
"path": "projectA",
"default_file": "main.tf",
"skip": false
},
{
"name": "Project B",
"default_file": "main.tf",
"path": "projectB",
"skip": false,
"var_file": "variables.tf",
"environments": [
{
"name": "dev",
"tfvars_file": "dev.tfvars"
},
{
"name": "stage",
"tfvars_file": "stage.tfvars"
},
{
"name": "prod",
"tfvars_file": "prod.tfvars"
}
]
}
]
}
Response
On success the following is expected
204 No Content
Getting Terraform configurations via the API
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
.
Supported values: ["TERRAFORM_CONFIG_SOURCE_SCAN", "TERRAFORM_CONFIG_SOURCE_API"]
config_root_paths
(list(string), optional): Config root paths relative to the repository root.
Response
On success the following response type is expected
200 OK
{
"repo_url": "https://github.com/ResourcelyTest/terraform",
"terraform_configs": [
{
"terraform_config": {
"name": "Project A",
"description": "",
"type": "simple",
"path": "projectA",
"skip": false,
"default_file": "main.tf",
"var_file": "",
"extra_tfvars_files": [],
"context": null,
"environments": [],
"module_invocations": []
},
"version": "2",
"source": "TERRAFORM_CONFIG_SOURCE_API"
},
{
"terraform_config": {
"name": "Project B",
"description": "",
"type": "simple",
"path": "projectB",
"skip": false,
"default_file": "main.tf",
"var_file": "variables.tf",
"extra_tfvars_files": [],
"context": null,
"environments": [
{
"name": "dev",
"tfvars_file": "dev.tfvars",
"extra_tfvars_files": [],
"context": null
},
{
"name": "stage",
"tfvars_file": "stage.tfvars",
"extra_tfvars_files": [],
"context": null
}
],
"module_invocations": []
},
"version": "2",
"source": "TERRAFORM_CONFIG_SOURCE_API"
}
]
}
Validate resourcely.yaml via CLI
To validate one or more .resourcely.yaml
terraform configurations run the following:
Generate Resourcely API Token from Settings -> Generate API token
Set the token environment variable
Export RESOURCELY_API_TOKEN=<TOKEN>
Run the
validate
command
resourcely-cli validate repoconfig --file /path/to/.resourcely.yaml --file /path/to/another/.resourcely.yaml
Must be run from the git repository's root and file paths must also be relative to the git repository's root
Last updated