Stack Manifest Templating
Atmos supports Go templates in stack manifests.
Sprig Functions, Gomplate Functions and Gomplate Datasources are supported as well.
Important Note
Atmos supports many different ways of configuring and using Go
templates:
- In Atmos Custom Commands
- In Atmos Vendoring
- In Atmos Component Vendoring
- In Imports
- In Stack Manifests
These templates are processed in different phases and use different context:
-
Go
templates in Atmos Custom Commands are processed when the custom commands are executed. The execution context can be specified by using thecomponent_config
section. If a custom command defines acomponent_config
section withcomponent
andstack
, Atmos generates the config for the component in the stack and makes it available in the{{ .ComponentConfig.xxx.yyy.zzz }}
template variables, exposing all the component sections that are returned by theatmos describe component <component> -s <stack>
CLI command -
Go
templates in Atmos Vendoring and Atmos Component Vendoring are processed when the CLI commandatmos vendor pull
is executed. The templates in the vendoring manifests support the{{.Version}}
variable, and the execution context is provided in theversion
attribute -
Go
Templates in Imports are used in imported stack manifests to make them DRY and reusable. The context (variables) for theGo
templates is provided via the staticcontext
section. Atmos processesGo
templates in imports as the very first phase of the stack processing pipeline. When executing the CLI commands, Atmos parses and executes the templates using the provided staticcontext
, processes all imports, and finds stacks and components -
Go
templates in Atmos stack manifests, on the other hand, are processed as the very last phase of the stack processing pipeline (after all imports are processed, all stack configurations are deep-merged, and the component in the stack is found). For the context (template variables), it uses all the component's attributes returned from theatmos describe component
CLI command
These mechanisms, although all using Go
templates, serve different purposes, use different contexts, and are executed
in different phases of the stack processing pipeline.
For more details, refer to:
Stack Manifest Templating Configuration
Templating in Atmos stack manifests can be configured in the following places:
-
In the
templates.settings
section inatmos.yaml
CLI config file -
In the
settings.templates.settings
section in Atmos stack manifests. Thesettings.templates.settings
section can be defined globally per organization, tenant, account, or per component. Atmos deep-merges the configurations from all scopes into the final result using inheritance.
Configuring Templating in atmos.yaml
CLI Config File
Templating in Atmos stack manifests is configured in the atmos.yaml
CLI config file in the
templates.settings
section:
# https://pkg.go.dev/text/template
templates:
settings:
# Enable `Go` templates in Atmos stack manifests
enabled: true
# Number of evaluations/passes to process `Go` templates
# If not defined, `evaluations` is automatically set to `1`
evaluations: 2
# Optional template delimiters
# The `{{ }}` delimiters are the default, no need to specify/redefine them
delimiters: ["{{", "}}"]
# Environment variables passed to datasources when evaluating templates
# https://docs.gomplate.ca/datasources/#using-awssmp-datasources
# https://docs.gomplate.ca/functions/aws/#configuring-aws
# https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html
env:
AWS_PROFILE: "<AWS profile>"
AWS_TIMEOUT: 2000
# https://masterminds.github.io/sprig
sprig:
# Enable Sprig functions in `Go` templates in Atmos stack manifests
enabled: true
# https://docs.gomplate.ca
# https://docs.gomplate.ca/functions
gomplate:
# Enable Gomplate functions and datasources in `Go` templates in Atmos stack manifests
enabled: true
# Timeout in seconds to execute the datasources
timeout: 5
# https://docs.gomplate.ca/datasources
datasources:
# 'http' datasource
# https://docs.gomplate.ca/datasources/#using-file-datasources
ip:
url: "https://api.ipify.org?format=json"
# https://docs.gomplate.ca/datasources/#sending-http-headers
# https://docs.gomplate.ca/usage/#--datasource-header-h
headers:
accept:
- "application/json"
# 'file' datasources
# https://docs.gomplate.ca/datasources/#using-file-datasources
config-1:
url: "./config1.json"
config-2:
url: "file:///config2.json"
# `aws+smp` AWS Systems Manager Parameter Store datasource
# https://docs.gomplate.ca/datasources/#using-awssmp-datasources
secret-1:
url: "aws+smp:///path/to/secret"
# `aws+sm` AWS Secrets Manager datasource
# https://docs.gomplate.ca/datasources/#using-awssm-datasource
secret-2:
url: "aws+sm:///path/to/secret"
# `s3` datasource
# https://docs.gomplate.ca/datasources/#using-s3-datasources
s3-config:
url: "s3://mybucket/config/config.json"
-
templates.settings.enabled
- a boolean flag to enable/disable the processing ofGo
templates in Atmos stack manifests. If set tofalse
, Atmos will not processGo
templates in stack manifests -
templates.settings.env
- a map of environment variables to use when executing the templates -
templates.settings.evaluations
- number of evaluations/passes to processGo
templates. If not defined,evaluations
is automatically set to1
. For more details, refer to Template Evaluations and Template Processing Pipelines -
templates.settings.delimiters
- a list of left and right delimiters to use to process the templates. If not defined, the defaultGo
template delimiters["{{", "}}"]
will be used -
templates.settings.sprig.enabled
- a boolean flag to enable/disable the Sprig Functions in Atmos stack manifests -
templates.settings.gomplate.enabled
- a boolean flag to enable/disable the Gomplate Functions and Gomplate Datasources in Atmos stack manifests -
templates.settings.gomplate.timeout
- timeout in seconds to execute Gomplate Datasources -
templates.settings.gomplate.datasources
- a map of Gomplate Datasource definitions:-
The keys of the map are the datasource names (aliases), which are used in
Go
templates in Atmos stack manifests. For example:terraform:
vars:
tags:
provisioned_by_ip: '{{ (datasource "ip").ip }}'
config1_tag: '{{ (datasource "config-1").tag }}'
config2_service_name: '{{ (datasource "config-2").service.name }}' -
The values of the map are the datasource definitions with the following schema:
-
url
- the Datasource URL -
headers
- a map of HTTP request headers for thehttp
datasource. The keys of the map are the header names. The values of the map are lists of values for the header.The following configuration will result in the
accept: application/json
HTTP header being sent with the HTTP request to the datasource:headers:
accept:
- "application/json"
-
-
Some functions are present in both Sprig and Gomplate.
For example, the env
function has the same name in Sprig and
Gomplate, but has different syntax and accept different number of arguments.
If you use the env
function from one templating engine and enable both Sprig
and Gomplate, it will be invalid in the other templating engine, and an error will be thrown.
To be able to use the env
function from both templating engines, you can do one of the following:
-
Use the
env
function from one templating engine, and disable the other templating engine by using thetemplates.settings.sprig.enabled
andtemplates.settings,gomplate.enabled
settings -
Enable both engines and use the Gomplate's
env
function via itsgetenv
alias
Configuring Templating in Atmos Stack Manifests
Templating in Atmos can also be configured in the settings.templates.settings
section in stack manifests.
The settings.templates.settings
section can be defined globally per organization, tenant, account, or per component.
Atmos deep-merges the configurations from all scopes into the final result using inheritance.
The schema is the same as templates.settings
in the atmos.yaml
CLI config file,
except the following settings are not supported in the settings.templates.settings
section:
settings.templates.settings.enabled
settings.templates.settings.sprig.enabled
settings.templates.settings.gomplate.enabled
settings.templates.settings.evaluations
settings.templates.settings.delimiters
These settings are not supported for the following reasons:
-
You can't disable templating in the stack manifests which are being processed by Atmos as
Go
templates -
If you define the
delimiters
in thesettings.templates.settings
section in stack manifests, theGo
templating engine will think that the delimiters specify the beginning and the end of template strings, will try to evaluate it, which will result in an error
As an example, let's define templating configuration for the entire organization in the stacks/orgs/acme/_defaults.yaml
stack manifest:
settings:
templates:
settings:
# Environment variables passed to datasources when evaluating templates
# https://docs.gomplate.ca/datasources/#using-awssmp-datasources
# https://docs.gomplate.ca/functions/aws/#configuring-aws
# https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html
env:
AWS_PROFILE: "<AWS profile>"
AWS_TIMEOUT: 2000
gomplate:
# 7 seconds timeout to execute the datasources
timeout: 7
# https://docs.gomplate.ca/datasources
datasources:
# 'file' datasources
# https://docs.gomplate.ca/datasources/#using-file-datasources
config-1:
url: "./my-config1.json"
config-3:
url: "file:///config3.json"
Atmos deep-merges the configurations from the settings.templates.settings
section in Atmos stack manifests
with the templates.settings
section in atmos.yaml
CLI config file using inheritance.
The settings.templates.settings
section in Atmos stack manifests takes precedence over
the templates.settings
section in atmos.yaml
CLI config file, allowing you to define the global
datasources
in atmos.yaml
and then add or override datasources
in Atmos stack manifests for the entire organization,
tenant, account, or per component.
For example, taking into account the configurations described above in atmos.yaml
CLI config file
and in the stacks/orgs/acme/_defaults.yaml
stack manifest, the final datasources
map will look like this:
gomplate:
timeout: 7
datasources:
ip:
url: "https://api.ipify.org?format=json"
headers:
accept:
- "application/json"
random:
url: "http://www.randomnumberapi.com/api/v1.0/randomstring?min=${ .settings.random.min }&max=${ .settings.random.max }&count=1"
secret-1:
url: "aws+smp:///path/to/secret"
secret-2:
url: "aws+sm:///path/to/secret"
s3-config:
url: "s3://mybucket/config/config.json"
config-1:
url: "./my-config1.json"
config-2:
url: "file:///config2.json"
config-3:
url: "file:///config3.json"
Note that the config-1
datasource from atmos.yaml
was overridden with the config-1
datasource from the
stacks/orgs/acme/_defaults.yaml
stack manifest. The timeout
attribute was overridden as well.
You can now use the datasources
in Go
templates in all Atmos sections that support Go
templates.
Atmos sections supporting Go
templates
You can use Go
templates in the following Atmos sections to refer to values in the same or other sections:
vars
settings
env
metadata
providers
overrides
backend
backend_type
In the template tokens, you can refer to any value in any section that the Atmos command
atmos describe component <component> -s <stack>
generates
For example, let's say we have the following component configuration using Go
templates:
component:
terraform:
vpc:
settings:
setting1: 1
setting2: 2
setting3: "{{ .vars.var3 }}"
setting4: "{{ .settings.setting1 }}"
component: vpc
backend_type: s3
region: "us-east-2"
assume_role: "<role-arn>"
backend_type: "{{ .settings.backend_type }}"
metadata:
component: "{{ .settings.component }}"
providers:
aws:
region: "{{ .settings.region }}"
assume_role: "{{ .settings.assume_role }}"
env:
ENV1: e1
ENV2: "{{ .settings.setting1 }}-{{ .settings.setting2 }}"
vars:
var1: "{{ .settings.setting1 }}"
var2: "{{ .settings.setting2 }}"
var3: 3
# Add the tags to all the resources provisioned by this Atmos component
tags:
atmos_component: "{{ .atmos_component }}"
atmos_stack: "{{ .atmos_stack }}"
atmos_manifest: "{{ .atmos_stack_file }}"
region: "{{ .vars.region }}"
terraform_workspace: "{{ .workspace }}"
assumed_role: "{{ .providers.aws.assume_role }}"
description: "{{ .atmos_component }} component provisioned in {{ .atmos_stack }} stack by assuming IAM role {{ .providers.aws.assume_role }}"
# Examples of using the Sprig and Gomplate functions and datasources
# https://masterminds.github.io/sprig/os.html
provisioned_by_user: '{{ env "USER" }}'
# https://docs.gomplate.ca/functions/strings
atmos_component_description: "{{ strings.Title .atmos_component }} component {{ .vars.name | strings.Quote }} provisioned in the stack {{ .atmos_stack | strings.Quote }}"
# https://docs.gomplate.ca/datasources
provisioned_by_ip: '{{ (datasource "ip").ip }}'
config1_tag: '{{ (datasource "config-1").tag }}'
config2_service_name: '{{ (datasource "config-2").service.name }}'
config3_team_name: '{{ (datasource "config-3").team.name }}'
When executing Atmos commands like atmos describe component
and atmos terraform plan/apply
, Atmos processes all the template tokens
in the manifest and generates the final configuration for the component in the stack:
settings:
setting1: 1
setting2: 2
setting3: 3
setting4: 1
component: vpc
backend_type: s3
region: us-east-2
assume_role: <role-arn>
backend_type: s3
metadata:
component: vpc
providers:
aws:
region: us-east-2
assume_role: <role-arn>
env:
ENV1: e1
ENV2: 1-2
vars:
var1: 1
var2: 2
var3: 3
tags:
assumed_role: <role-arn>
atmos_component: vpc
atmos_component_description: Vpc component "common" provisioned in the stack "plat-ue2-dev"
atmos_manifest: orgs/acme/plat/dev/us-east-2
atmos_stack: plat-ue2-dev
config1_tag: test1
config2_service_name: service1
config3_team_name: my-team
description: vpc component provisioned in plat-ue2-dev stack by assuming IAM role <role-arn>
provisioned_by_user: <user>
provisioned_by_ip: 167.38.132.237
region: us-east-2
terraform_workspace: plat-ue2-dev
Environment Variables
Some data sources might need environment variables that are different from the environment variables in Stack configuration. Environment variables may be passed to data soruces when processing and executing templates by defining env
map.
It's supported in both the templates.settings
section in atmos.yaml
CLI config file and in the
settings.templates.settings
section in Atmos stack manifests.
For example:
settings:
templates:
settings:
# Environment variables passed to datasources when evaluating templates
# https://docs.gomplate.ca/datasources/#using-awssmp-datasources
# https://docs.gomplate.ca/functions/aws/#configuring-aws
# https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html
env:
AWS_PROFILE: "<AWS profile>"
AWS_TIMEOUT: 2000
This is useful when executing the datasources
that need to authenticate to cloud APIs.
For more details, refer to:
Datasources
Currently, Atmos supports all the Gomplate Datasources. More datasources will be added in the future (and this doc will be updated).
The Gomplate Datasources can be configured in the
templates.settings.gomplate.datasources
section in atmos.yaml
CLI config file and in the settings.templates.settings
section in
Atmos stack manifests.
The templates.settings.gomplate.datasources
section is a map of objects.
The keys of the map are the datasource names (aliases).
The values of the map are the datasource definitions with the following schema:
-
url
- the Datasource URL -
headers
- a map of HTTP request headers for thehttp
datasource. The keys of the map are the header names. The values of the map are lists of values for the header.The following configuration will result in the
accept: application/json
HTTP header being sent with the HTTP request to the datasource:headers:
accept:
- "application/json"
For example, let's define the following Gomplate datasources in the global settings
section (this will apply to all
components in all stacks in the infrastructure):
settings:
templates:
settings:
gomplate:
# Timeout in seconds to execute the datasources
timeout: 5
# https://docs.gomplate.ca/datasources
datasources:
# 'http' datasource
# https://docs.gomplate.ca/datasources/#using-file-datasources
ip:
url: "https://api.ipify.org?format=json"
# https://docs.gomplate.ca/datasources/#sending-http-headers
# https://docs.gomplate.ca/usage/#--datasource-header-h
headers:
accept:
- "application/json"
# 'file' datasources
# https://docs.gomplate.ca/datasources/#using-file-datasources
config-1:
url: "./config1.json"
config-2:
url: "file:///config2.json"
# `aws+smp` AWS Systems Manager Parameter Store datasource
# https://docs.gomplate.ca/datasources/#using-awssmp-datasources
secret-1:
url: "aws+smp:///path/to/secret"
# `aws+sm` AWS Secrets Manager datasource
# https://docs.gomplate.ca/datasources/#using-awssm-datasource
secret-2:
url: "aws+sm:///path/to/secret"
# `s3` datasource
# https://docs.gomplate.ca/datasources/#using-s3-datasources
s3-config:
url: "s3://mybucket/config/config.json"
After the above datasources
are defined, you can use them in Atmos stack manifests like this:
terraform:
vars:
tags:
tag1: '{{ (datasource "config-1").tag }}'
service_name2: '{{ (datasource "config-2").service.name }}'
service_name3: '{{ (datasource "s3-config").config.service_name }}'
components:
terraform:
vpc-1:
settings:
provisioned_by_ip: '{{ (datasource "ip").ip }}'
secret-1: '{{ (datasource "secret-1").secret1.value }}'
vars:
enabled: '{{ (datasource "config-2").config.enabled }}'
Use-cases
While Go
templates in Atmos stack manifests offer great flexibility for various use-cases, one of the obvious use-cases
is to add a standard set of tags to all the resources in the infrastructure.
For example, by adding this configuration to the stacks/orgs/acme/_defaults.yaml
Org-level stack manifest:
terraform:
vars:
tags:
atmos_component: "{{ .atmos_component }}"
atmos_stack: "{{ .atmos_stack }}"
atmos_manifest: "{{ .atmos_stack_file }}"
terraform_workspace: "{{ .workspace }}"
# Examples of using the Gomplate and Sprig functions
# https://docs.gomplate.ca/functions/strings
atmos_component_description: "{{ strings.Title .atmos_component }} component {{ .vars.name | strings.Quote }} provisioned in the stack {{ .atmos_stack | strings.Quote }}"
# https://masterminds.github.io/sprig/os.html
provisioned_by_user: '{{ env "USER" }}'
The tags will be processed and automatically added to all the resources provisioned in the infrastructure.
Excluding Templates in Stack Manifest from Processing by Atmos
If you need to provide Go
templates to external systems (e.g. ArgoCD or Datadog) verbatim and prevent Atmos from
processing the templates, use double curly braces + backtick + double curly braces instead of just double curly braces:
{{`{{ instead of {{
}}`}} instead of }}
For example:
components:
terraform:
eks/argocd:
metadata:
component: "eks/argocd"
vars:
enabled: true
name: "argocd"
chart_repository: "https://argoproj.github.io/argo-helm"
chart_version: 5.46.0
chart_values:
template-github-commit-status:
message: |
Application {{`{{ .app.metadata.name }}`}} is now running new version.
webhook:
github-commit-status:
method: POST
path: "/repos/{{`{{ call .repo.FullNameByRepoURL .app.metadata.annotations.app_repository }}`}}/statuses/{{`{{ .app.metadata.annotations.app_commit }}`}}"
body: |
{
{{`{{ if eq .app.status.operationState.phase "Running" }}`}} "state": "pending"{{`{{end}}`}}
{{`{{ if eq .app.status.operationState.phase "Succeeded" }}`}} "state": "success"{{`{{end}}`}}
{{`{{ if eq .app.status.operationState.phase "Error" }}`}} "state": "error"{{`{{end}}`}}
{{`{{ if eq .app.status.operationState.phase "Failed" }}`}} "state": "error"{{`{{end}}`}},
"description": "ArgoCD",
"target_url": "{{`{{ .context.argocdUrl }}`}}/applications/{{`{{ .app.metadata.name }}`}}",
"context": "continuous-delivery/{{`{{ .app.metadata.name }}`}}"
}
When Atmos processes the templates in the manifest shown above, it renders them as raw strings allowing sending the templates to the external system for processing:
chart_values:
template-github-commit-status:
message: |
Application {{ .app.metadata.name }} is now running new version.
webhook:
github-commit-status:
method: POST
path: "/repos/{{ call .repo.FullNameByRepoURL .app.metadata.annotations.app_repository }}/statuses/{{ .app.metadata.annotations.app_commit }}"
body: |
{
{{ if eq .app.status.operationState.phase "Running" }} "state": "pending"{{end}}
{{ if eq .app.status.operationState.phase "Succeeded" }} "state": "success"{{end}}
{{ if eq .app.status.operationState.phase "Error" }} "state": "error"{{end}}
{{ if eq .app.status.operationState.phase "Failed" }} "state": "error"{{end}},
"description": "ArgoCD",
"target_url": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"context": "continuous-delivery/{{ .app.metadata.name }}"
}
The printf
template function is also supported and can be used instead of double curly braces + backtick + double curly braces.
The following examples produce the same result:
chart_values:
template-github-commit-status:
message: >-
Application {{`{{ .app.metadata.name }}`}} is now running new version.
chart_values:
template-github-commit-status:
message: "Application {{`{{ .app.metadata.name }}`}} is now running new version."
chart_values:
template-github-commit-status:
message: >-
{{ printf "Application {{ .app.metadata.name }} is now running new version." }}
chart_values:
template-github-commit-status:
message: '{{ printf "Application {{ .app.metadata.name }} is now running new version." }}'
Excluding Templates in Imports from Processing by Atmos
If you are using Go
Templates in Imports and Go
templates
in stack manifests in the same Atmos manifest, take into account that in this case Atmos will do Go
template processing two times (two passes):
- When importing the manifest and processing the template tokens using the variables from the provided
context
object - After finding the component in the stack as the final step in the processing pipeline
For example, we can define the following configuration in the stacks/catalog/eks/eks_cluster.tmpl
template file:
components:
terraform:
eks/cluster:
metadata:
component: eks/cluster
vars:
enabled: "{{ .enabled }}"
name: "{{ .name }}"
tags:
atmos_component: "{{ .atmos_component }}"
atmos_stack: "{{ .atmos_stack }}"
terraform_workspace: "{{ .workspace }}"
Then we import the template into a top-level stack providing the context variables for the import in the context
object:
import:
- path: "catalog/eks/eks_cluster.tmpl"
context:
enabled: true
name: prod-eks
Atmos will process the import and replace the template tokens using the variables from the context
.
Since the context
does not provide the variables for the template tokens in tags
, the following manifest will be
generated:
components:
terraform:
eks/cluster:
metadata:
component: eks/cluster
vars:
enabled: true
name: prod-eks
tags:
atmos_component: <no value>
atmos_stack: <no value>
terraform_workspace: <no value>
The second pass of template processing will not replace the tokens in tags
because they are already processed in the
first pass (importing) and the values <no value>
are generated.
To deal with this, use double curly braces + backtick + double curly braces instead of just double curly braces
in tags
to prevent Atmos from processing the templates in the first pass and instead process them in the second pass:
components:
terraform:
eks/cluster:
metadata:
component: eks/cluster
vars:
enabled: "{{ .enabled }}"
name: "{{ .name }}"
tags:
atmos_component: "{{`{{ .atmos_component }}`}}"
atmos_stack: "{{`{{ .atmos_stack }}`}}"
terraform_workspace: "{{`{{ .workspace }}`}}"
Atmos will first process the import and replace the template tokens using the variables from the context
.
Then in the second pass the tokens in tags
will be replaced with the correct values.
It will generate the following manifest:
components:
terraform:
eks/cluster:
metadata:
component: eks/cluster
vars:
enabled: true
name: prod-eks
tags:
atmos_component: eks/cluster
atmos_stack: plat-ue2-prod
terraform_workspace: plat-ue2-prod
Template Evaluations and Template Processing Pipelines
Atmos supports configuring the number of evaluations/passes for template processing in atmos.yaml
CLI config file.
It effectively allows you to define template processing pipelines.
For example:
templates:
settings:
# Enable `Go` templates in Atmos stack manifests
enabled: true
# Number of evaluations/passes to process `Go` templates
# If not defined, `evaluations` is automatically set to `1`
evaluations: 2
templates.settings.evaluations
- number of evaluations to processGo
templates. If not defined,evaluations
is automatically set to1
Template evaluations are useful in the following scenarios:
- Combining templates from different sections in Atmos stack manifests
- Using templates in the URLs of
datasources
Combining templates from different sections in Atmos stack manifests
You can define more than one step/pass of template processing to use and combine the results from each step.
For example:
templates:
settings:
enabled: true
# Number of evaluations to process `Go` templates
evaluations: 3
settings:
test: "{{ .atmos_component }}"
test2: "{{ .settings.test }}"
components:
terraform:
vpc:
vars:
tags:
tag1: "{{ .settings.test }}-{{ .settings.test2 }}"
tag2: "{{\"{{`{{ .atmos_component }}`}}\"}}"
When executing an Atmos command like atmos terraform plan vpc -s <stack>
, the above template will be processed
in three phases:
-
Evaluation 1
settings.test
is set tovpc
settings.test2
is set to{{ .atmos_component }}
vpc.vars.tags.tag1
is set to{{ .atmos_component }}-{{ .settings.test }}
vpc.vars.tags.tag2
is set to{{<backtick>{{ .atmos_component }}<backtick>}}
-
Evaluation 2
settings.test
isvpc
settings.test2
is set tovpc
vpc.vars.tags.tag1
is set tovpc-vpc
vpc.vars.tags.tag2
is set to{{ .atmos_component }}
-
Evaluation 3
settings.test
isvpc
settings.test2
isvpc
vpc.vars.tags.tag1
isvpc-vpc
vpc.vars.tags.tag2
is set tovpc
The above example shows the supported functionality in Atmos templating. You can use it for some use-cases, but it does not mean that you should use it just for the sake of using, since it's not easy to read and understand what data we have after each evaluation step.
The following use-case describes a practical approach to using evaluation steps in Atmos templates to work
with datasources
.
Using templates in the URLs of datasources
Let's suppose that your company uses a centralized software catalog to consolidate all tags for tagging all the cloud resources. The tags can include tags per account, per team, per service, billing tags, etc.
An example of such a centralized software catalog could be https://backstage.io
Let's also suppose that you have a service to read the tags from the centralized catalog and write them into an S3 bucket in one of your accounts. The bucket serves as a cache to not hit the external system's API with too many requests and not to trigger rate limiting.
And finally, let's say that in the bucket, you have folders per account (dev
, prod
, staging
). Each folder has a JSON
file with all the tags defined for all the cloud resources in the accounts.
We can then use the Gomplate S3 datasource to read the JSON file with the tags for each account and assign the tags to all cloud resources.
In atmos.yaml
, we figure two evaluations steps of template processing:
templates:
settings:
enabled: true
# Number of evaluations to process `Go` templates
evaluations: 2
gomplate:
enabled: true
In an Atmos stack manifest, we define the environment variables in the env
section (AWS profile with permissions to
access the S3 bucket), and the s3-tags
Gomplate datasource.
In the terraform.vars.tags
section, we define all the tags that are returned from the call to the S3 datasource.
import:
# Import the default configuration for all VPCs in the infrastructure
- catalog/vpc/defaults
# Global settings
settings:
templates:
settings:
# Environment variables passed to datasources when evaluating templates
# https://docs.gomplate.ca/functions/aws/#configuring-aws
# https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html
env:
# AWS profile with permissions to access the S3 bucket
AWS_PROFILE: "<AWS profile>"
gomplate:
# Timeout in seconds to execute the datasources
timeout: 5
# https://docs.gomplate.ca/datasources
datasources:
# `s3` datasource
# https://docs.gomplate.ca/datasources/#using-s3-datasources
s3-tags:
# The `url` uses a `Go` template with the delimiters `${ }`,
# which is processed as first step in the template processing pipeline
url: "s3://mybucket/{{ .vars.stage }}/tags.json"
# Global Terraform config
terraform:
# Global variables that are used by all Atmos components
vars:
tags:
atmos_component: "{{ .atmos_component }}"
atmos_stack: "{{ .atmos_stack }}"
terraform_component: "{{ .component }}"
terraform_workspace: "{{ .workspace }}"
devops_team: '{{`{{ (datasource "s3-tags").tags.devops_team }}`}}'
billing_team: '{{`{{ (datasource "s3-tags").tags.billing_team }}`}}'
service: '{{`{{ (datasource "s3-tags").tags.service }}`}}'
# Atmos component configurations
components:
terraform:
vpc/1:
metadata:
component: vpc # Point to the Terraform component in `components/terraform/vpc` folder
inherits:
# Inherit from the `vpc/defaults` base Atmos component, which defines the default
# configuration for all VPCs in the infrastructure.
# The `vpc/defaults` base component is defined in the `catalog/vpc/defaults`
# manifest (which is imported above).
# This inheritance makes the `vpc/1` Atmos component config DRY.
- "vpc/defaults"
vars:
name: "vpc-1"
When executing an Atmos command like atmos terraform apply vpc/1 -s plat-ue2-dev
, the above template will be processed
in two evaluation steps:
-
Evaluation 1:
-
datasources.s3-tags.url
is set tos3://mybucket/dev/tags.json
-
the tags that use the
datasource
templates are set to the following:devops_team: '{{ (datasource "s3-tags").tags.devops_team }}'
billing_team: '{{ (datasource "s3-tags").tags.billing_team }}'
service: '{{ (datasource "s3-tags").tags.service }}'
-
-
Evaluation 2:
- all
s3-tags
datasources get executed, the JSON files3://mybucket/dev/tags.json
with the tags for thedev
account is downloaded from the S3 bucket, and the tags are parsed and assigned in theterraform.vars.tags
section
- all
After executing the two evaluation steps, the resulting tags for the Atmos component vpc/1
in the stack plat-ue2-dev
would look like this:
atmos_component: vpc/1
atmos_stack: plat-ue2-dev
terraform_component: vpc
terraform_workspace: plat-ue2-dev-vpc-1
devops_team: dev_networking
billing_team: billing_net
service: net
The tags will be added to all the AWS resources provisioned by the vpc
Terraform component in the plat-ue2-dev
stack.