Boilerplate Templates
Gruntwork Boilerplate generates files and folders from templates. Runbooks takes two things from it: the boilerplate.yml schema that <Inputs> and <Template> build their forms from, and the Go template syntax that <Template>, <TemplateInline>, <Command>, and <Check> render.
This page covers the parts of Boilerplate that runbook authors use. The Boilerplate repo has the complete documentation.
Boilerplate in a nutshell
Section titled “Boilerplate in a nutshell”Boilerplate is a template engine, similar to Jinja, Nunjucks, or Cookiecutter.
Why Boilerplate
Section titled “Why Boilerplate”Boilerplate is built for DevOps and infrastructure work:
- As a CLI, it prompts for the variables defined in
boilerplate.yml, or takes them from command-line options so it can run in automated tests. - Templates are Go templates, with conditionals, loops, and helpers for loading another file, running a shell command, and including partial templates.
- A template can depend on other templates and include them conditionally on variable values.
- Variables are typed and can have validations.
- Hooks run arbitrary scripts.
- It is a standalone binary for macOS, Linux, and Windows.
Quick example
Section titled “Quick example”Say you want to generate a README for new projects. Create a template folder:
Directorymy-template/
- boilerplate.yml
- README.md
boilerplate.yml defines the variables:
variables: - name: ProjectName type: string description: Name of the project
- name: Author type: string description: Who is the author? default: AnonymousREADME.md is the template file:
# {{ .ProjectName }}
Created by {{ .Author }}.Run Boilerplate on the command line:
boilerplate \ --template-url ./my-template \ --output-folder ./output \ --var ProjectName="My Cool App" \ --var Author="Jane Doe"The result is output/README.md:
# My Cool App
Created by Jane Doe.The boilerplate.yml file
Section titled “The boilerplate.yml file”Every template directory needs a boilerplate.yml file that defines its variables. Runbooks renders a form from it.
Basic structure
Section titled “Basic structure”variables: - name: ProjectName type: string description: What would you like to call your project? default: my-project validations: - required
- name: Environment type: enum description: Which environment is this for? options: - dev - staging - prod default: devThis generates a form with a text input for ProjectName and a dropdown for Environment.
Variable properties
Section titled “Variable properties”Each variable supports these properties:
| Property | Required | Description |
|---|---|---|
name |
Yes | Variable name (used in templates as .Name) |
type |
No | Data type: string, int, float, bool, enum, list, map (defaults to string) |
description |
No | Help text shown in the form |
default |
No | Default value |
options |
For enum |
List of allowed values for dropdowns |
validations |
No | Validation rules |
sensitive |
No | For a string, masks the field like a password input, with a button to show the value |
Defaults that use other variables
Section titled “Defaults that use other variables”A default can use other variables, with the same template syntax as your template files:
variables: - name: ProjectName type: string default: acme
- name: Environment type: enum options: [dev, prod] default: dev
- name: BucketName type: string default: "{{ .ProjectName }}-state"
- name: InstanceSize type: string default: '{{ if eq .Environment "prod" }}large{{ else }}small{{ end }}'
- name: CatalogRepositories type: list default: - github.com/gruntwork-io/terraform-aws-service-catalog - "github.com/{{ .ProjectName }}/modules"
- name: DefaultTags type: map default: "{{ .ProjectName }}:Team": DevOpsRunbooks fills these in, using whatever the other fields hold at that moment, in two places: when it renders the body of a <Template> or <TemplateInline> block, and when a <Command> or <Check> script reads the variable through inputsId.
Other block props that use the variable through {{ .inputs.X }}, like <GitClone>’s prefilledUrl or <TemplateInline>’s outputPath, get the expression as written, not the value it resolves to. Give those props a variable with a plain default.
The form doesn’t show the {{ }} syntax. A linked default shows the value it comes to, with a link icon, and follows the fields it uses as you edit them. Here BucketName shows acme-state, and InstanceSize shows small until you pick prod. In a <Template>, a default can also use the values the block gets through inputsId.
When the form can’t work out the value yet, it shows a token for each variable the default uses instead, named the way that variable’s field is labelled: a Project Name token followed by -state. A default that does more than insert a variable shows one token that says what it’s based on, like Based on Environment. If the default uses a block output that hasn’t been produced yet, its token is yellow; hover over it to see which block has to run first. If no block on the page has the id the default names, the value can never be filled in: the token is red, the field shows an error, the block is red and won’t generate until you replace the value, and the error is counted in the runbook’s error summary.
A sensitive field, and any default built from a sensitive variable or output, always shows tokens, never the value.
In a text field:
- Hover over the value to see its expression.
- Click it to edit the expression. When you leave the field, a value that is still an expression is shown as linked again.
- Click the X to clear it and type your own value.
The template-defaults demo runbook uses each kind.
Variable types
Section titled “Variable types”string
Section titled “string”Text input field.
- name: BucketName type: string description: Name for the S3 bucket default: my-bucketNumeric input field.
- name: InstanceCount type: int description: Number of instances to deploy default: 3Numeric input field that accepts decimals.
- name: ScalingThreshold type: float description: CPU utilization that triggers scaling default: 0.75Checkbox toggle. Without a default, the checkbox starts unchecked, which is the value false.
- name: EnableLogging type: bool description: Enable CloudWatch logging? default: trueDropdown select from predefined options. Without a default, the dropdown starts empty and the variable has no value until the user picks one. With the required validation, the form asks for a pick before it can be submitted. Boilerplate itself needs a value for every variable without a default, so give an optional enum a default ("" works if "" is one of the options).
- name: Region type: enum description: AWS region for deployment options: - us-east-1 - us-west-2 - eu-west-1 default: us-east-1List of values. Users can add and remove items.
- name: AllowedIPs type: list description: IP addresses to allow default: []Key-value pairs. Users can add and remove entries.
- name: Tags type: map description: Resource tags default: {}For structured values, see x-schema.
Validations
Section titled “Validations”Add validation rules to a variable:
- name: ProjectName type: string validations: - required
- name: ContactEmail type: string validations: - email
- name: WebsiteURL type: string validations: - url
- name: Identifier type: string validations: - alphanumeric
- name: CountryCode type: string validations: - countrycode2
- name: Version type: string validations: - semver
- name: ManyValidations type: string validations: - "required" - "email"Custom error messages
Section titled “Custom error messages”Use the object form with type and message:
variables: - name: Email type: string validations: - type: required message: Email is required - type: email message: Must be a valid email addressValidation types
Section titled “Validation types”| Validation | Description |
|---|---|
required |
Field cannot be empty |
email |
Must be a valid email address |
url |
Must be a valid URL |
alpha |
Only letters allowed (no numbers or special characters) |
digit |
Only digits allowed (0-9) |
alphanumeric |
Only letters and numbers allowed |
countrycode2 |
Must be a valid two-letter country code (ISO 3166-1 alpha-2) |
semver |
Must be a valid semantic version (e.g., 1.0.0, 2.1.3-beta) |
length(min, max) |
Value must be between min and max characters long |
regex("pattern") |
Value must match the given regular expression pattern |
Parameterized validations
Section titled “Parameterized validations” - name: ProjectSlug type: string validations: - regex("^[a-z][a-z0-9-]+$")
- name: Description type: string validations: - length(0, 256)Template syntax
Section titled “Template syntax”Template files use Go template syntax. Reference a variable from boilerplate.yml with dot notation. In a runbook, the same variable is also available as {{ .inputs.BucketName }}, as Inputs and outputs describes.
Variable substitution
Section titled “Variable substitution”resource "aws_s3_bucket" "main" { bucket = "{{ .BucketName }}-{{ .Environment }}"}Conditionals
Section titled “Conditionals”{{- if .EnableLogging }}resource "aws_cloudwatch_log_group" "main" { name = "/app/{{ .ProjectName }}"}{{- end }}Comparisons
Section titled “Comparisons”instance_type = "{{ if eq .Environment "prod" }}t3.large{{ else }}t3.micro{{ end }}"{{- range .AllowedIPs }} - {{ . }}{{- end }}tags = {{{- range $key, $value := .Tags }} {{ $key }} = "{{ $value }}"{{- end }}}Whitespace control
Section titled “Whitespace control”Use - to trim whitespace around template directives:
{{-trims whitespace before-}}trims whitespace after
{{- if .Description }}description = "{{ .Description }}"{{- end }}Helper functions
Section titled “Helper functions”Boilerplate includes helper functions for common transformations:
# String case transformations{{ .ProjectName | snakeCase }} # my_project{{ .ProjectName | camelCase }} # myProject{{ .ProjectName | pascalCase }} # MyProject{{ .ProjectName | kebabCase }} # my-project{{ .ProjectName | upper }} # MY-PROJECT{{ .ProjectName | lower }} # my-project
# String checks{{ hasPrefix "prod" .Environment }} # true if starts with "prod"{{ hasSuffix "-dev" .Name }} # true if ends with "-dev"Dynamic file names
Section titled “Dynamic file names”File and folder names can use template syntax:
Directorytemplates/
- boilerplate.yml
{{ .ProjectName }}.tfDirectory
{{ .Environment }}/- config.yaml
With ProjectName: "vpc" and Environment: "prod", this generates:
- vpc.tf
Directoryprod/
- config.yaml
Runbooks extensions
Section titled “Runbooks extensions”Runbooks reads extra YAML properties, prefixed with x-, that change how the form renders. The Boilerplate CLI ignores them.
x-section
Section titled “x-section”x-section groups fields under a named heading. This form renders two sections, Basic Settings and Advanced Settings:
variables: - name: FunctionName type: string x-section: Basic Settings
- name: Runtime type: enum options: [python3.12, nodejs20.x] x-section: Basic Settings
- name: MemorySize type: int default: 128 x-section: Advanced Settings
- name: Timeout type: int default: 30 x-section: Advanced SettingsVariables without x-section appear in an unnamed section at the top.
x-options
Section titled “x-options”Lists the allowed values for a list variable. The form then renders a multi-select picker. The Boilerplate CLI doesn’t check the values against the list.
- name: Regions type: list description: Regions to deploy to x-options: - us-east-1 - us-west-2 - eu-west-1x-schema
Section titled “x-schema”A map variable collects key-value pairs where each value is a string. When each key needs several values, such as an email address, account ID, and name for each AWS account, give the map an x-schema. Runbooks then renders a structured form for each entry:
- name: AWSAccounts type: map description: AWS account configuration x-schema: email: string name: string id: stringEach map entry renders with those three fields.
x-schema-instance-label
Section titled “x-schema-instance-label”Sets the label for each entry of a schema-based map:
- name: AWSAccounts type: map description: AWS account configuration x-schema-instance-label: AWS Account Name x-schema: email: string environment: string id: stringAuthoring templates with the CLI
Section titled “Authoring templates with the CLI”You can run the Boilerplate CLI directly to test templates during development.
Install Boilerplate
Section titled “Install Boilerplate”# macOSbrew install gruntwork-io/tap/boilerplate
# Or download from GitHub releases# https://github.com/gruntwork-io/boilerplate/releasesGenerate files
Section titled “Generate files”boilerplate \ --template-url ./templates/vpc \ --output-folder ./output \ --var VpcName="my-vpc" \ --var Environment="dev"Interactive mode
Section titled “Interactive mode”Without --var flags, Boilerplate prompts for values interactively:
boilerplate \ --template-url ./templates/vpc \ --output-folder ./outputNon-interactive mode
Section titled “Non-interactive mode”Use --non-interactive with a vars file for CI/CD:
boilerplate \ --template-url ./templates/vpc \ --output-folder ./output \ --var-file vars.yml \ --non-interactiveExample template
Section titled “Example template”A template directory for a Lambda function:
Directorytemplates/
Directorylambda/
- boilerplate.yml
- main.tf
- variables.tf
- outputs.tf
boilerplate.yml:
variables: - name: FunctionName type: string description: Name for the Lambda function validations: - required x-section: Basic Settings
- name: Runtime type: enum description: Lambda runtime options: - python3.12 - nodejs20.x default: python3.12 x-section: Basic Settings
- name: MemorySize type: int description: Memory in MB (128-10240) default: 128 x-section: Advanced Settings
- name: EnableLogging type: bool description: Create CloudWatch log group? default: true x-section: Advanced Settings
- name: Tags type: map description: Resource tags default: {} x-section: Advanced Settingsmain.tf:
resource "aws_lambda_function" "main" { function_name = "{{ .FunctionName }}" runtime = "{{ .Runtime }}" memory_size = {{ .MemorySize }} handler = "index.handler" role = aws_iam_role.lambda.arn
tags = {{{- range $key, $value := .Tags }} {{ $key }} = "{{ $value }}"{{- end }} }}
{{- if .EnableLogging }}
resource "aws_cloudwatch_log_group" "lambda" { name = "/aws/lambda/{{ .FunctionName }}" retention_in_days = 14}{{- end }}Using templates in runbooks
Section titled “Using templates in runbooks”Reference a template directory from a runbook with <Template>:
# Deploy a Lambda Function
Configure your Lambda function below:
<Template id="lambda-config" path="templates/lambda" />
After generating, review the files in the file tree on the right.For a template that doesn’t need its own directory, pair <Inputs> with <TemplateInline>:
<Inputs id="config">```yamlvariables: - name: BucketName type: string```</Inputs>
<TemplateInline inputsId="config" outputPath="bucket.tf">```hclresource "aws_s3_bucket" "main" { bucket = "{{ .BucketName }}"}```</TemplateInline>