Skip to content

Create a smaller scale reference architecture example to highlight boilerplate features #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
defaults: &defaults
machine: true
environment:
GRUNTWORK_INSTALLER_VERSION: v0.0.21
MODULE_CI_VERSION: v0.27.2
TERRAFORM_VERSION: NONE
TERRAGRUNT_VERSION: NONE
GRUNTWORK_INSTALLER_VERSION: v0.0.36
MODULE_CI_VERSION: v0.43.1
TERRAFORM_VERSION: 1.0.11
TERRAGRUNT_VERSION: v0.36.1
PACKER_VERSION: NONE
GOLANG_VERSION: "1.14"
GOLANG_VERSION: "1.17"
GO111MODULE: auto

version: 2.1
jobs:
Expand Down Expand Up @@ -78,7 +79,7 @@ workflows:
only: /^v.*/
- deploy:
context:
- Gruntwork Admin
- Gruntwork Admin
requires:
- test
filters:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ The `boilerplate` binary supports the following options:

* `--template-url URL` (required): Generate the project from the templates in `URL`. This can be a local path, or a
[go-getter](https://github.com/hashicorp/go-getter) compatible URL for remote templates (e.g.,
`[email protected]:gruntwork-io/boilerplate.git//examples/include?ref=master`).
`[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/include?ref=master`).
* `--output-folder` (required): Create the output files and folders in `FOLDER`.
* `--non-interactive` (optional): Do not prompt for input variables. All variables must be set via `--var` and
`--var-file` options instead.
Expand Down Expand Up @@ -201,7 +201,7 @@ boilerplate --template-url ~/templates --output-folder ~/output --var-file vars.
Generate a project in ~/output from the templates in this repo's `include` example dir, using variables read from a file:

```
boilerplate --template-url "[email protected]:gruntwork-io/boilerplate.git//examples/include?ref=master" --output-folder ~/output --var-file vars.yml
boilerplate --template-url "[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/include?ref=master" --output-folder ~/output --var-file vars.yml
```


Expand Down
4 changes: 2 additions & 2 deletions cli/boilerplate_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Generate a project in ~/output from the templates in ~/templates, using variable

Generate a project in ~/output from the templates in this repo's include example dir, using variables read from a file:

boilerplate --template-url "[email protected]:gruntwork-io/boilerplate.git//examples/include?ref=master" --output-folder ~/output --var-file vars.yml
boilerplate --template-url "[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/include?ref=master" --output-folder ~/output --var-file vars.yml


Options:
Expand All @@ -52,7 +52,7 @@ func CreateBoilerplateCli(version string) *cli.App {
app.Flags = []cli.Flag{
cli.StringFlag{
Name: options.OptTemplateUrl,
Usage: "Generate the project from the templates in `URL`. This can be a local path, or a go-getter compatible URL for remote templates (e.g., `[email protected]:gruntwork-io/boilerplate.git//examples/include?ref=master`).",
Usage: "Generate the project from the templates in `URL`. This can be a local path, or a go-getter compatible URL for remote templates (e.g., `[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/include?ref=master`).",
},
cli.StringFlag{
Name: options.OptOutputFolder,
Expand Down
2 changes: 1 addition & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ func TestMarshalBoilerplateConfig(t *testing.T) {
t.Parallel()

marshalYamlTestExpectedBase := filepath.Join("..", "test-fixtures", "marshal-yaml-test")
examplesBase := filepath.Join("..", "examples")
examplesBase := filepath.Join("..", "examples", "for-learning-and-testing")

examplesToTest, err := ioutil.ReadDir(marshalYamlTestExpectedBase)
require.NoError(t, err)
Expand Down
7 changes: 0 additions & 7 deletions examples/dependencies-dynamic/README.md

This file was deleted.

5 changes: 0 additions & 5 deletions examples/dependencies-recursive/README.md

This file was deleted.

6 changes: 0 additions & 6 deletions examples/dependencies-remote/README.md

This file was deleted.

6 changes: 0 additions & 6 deletions examples/dependencies-varfile-precedence/README.md

This file was deleted.

6 changes: 0 additions & 6 deletions examples/dependencies-varfile/README.md

This file was deleted.

6 changes: 0 additions & 6 deletions examples/dependencies/README.md

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# {{ .Title }}

{{ .Description }}. It specifies both the
[docs](/examples/for-learning-and-testing/docs) and [website](/examples/for-learning-and-testing/website) examples as
dependencies to show how one boilerplate template can pull in another, and that you can use interpolation in the
`template-url` and `output-folder` parameters of dependencies to dynamically specify where to read the template and
where to write the output. It also defines all the variables needed for both of those dependencies at the top level to
show how variable inheritance works.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# {{ .Title }}

This is a boilerplate template that shows an example of using recursive dependencies. This template depends on the
[dependencies](/examples/for-learning-and-testing/dependencies) example, which, in turn has its own dependencies. This
template also defines all the variables needed for all dependencies to show how variable inheritance works.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# {{ .Title }}

{{ .Description }}. It specifies both the
[docs](/examples/for-learning-and-testing/docs) and [website](/examples/for-learning-and-testing/website) examples as
dependencies to show how one boilerplate template can pull in another. It also defines all the variables needed for both
of those dependencies at the top level to show how variable inheritance works.
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ variables:
type: bool
default: true

- name: RemoteBranch
description: The branch of boilerplate repo to use when pulling down remote dependencies.
default: "master"

dependencies:
- name: docs
template-url: "[email protected]:gruntwork-io/boilerplate.git//examples/docs?ref=master"
template-url: "[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/docs?ref={{ .RemoteBranch }}"
output-folder: ./docs
variables:
- name: Title
description: Enter the title of the docs page

- name: website
template-url: "[email protected]:gruntwork-io/boilerplate.git//examples/website?ref=master"
template-url: "[email protected]:gruntwork-io/boilerplate.git//examples/for-learning-and-testing/website?ref={{ .RemoteBranch }}"
output-folder: ./website
variables:
- name: Title
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# {{ .Title }}

{{ .Description }}. It specifies both the
[docs](/examples/for-learning-and-testing/docs) and [website](/examples/for-learning-and-testing/website) examples as
dependencies to show how one boilerplate template can pull in another. It also defines all the variables needed for both
of those dependencies at the top level to show how variable inheritance works.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# {{ .Title }}

{{ .Description }}. It specifies both the
[docs](/examples/for-learning-and-testing/docs) and [website](/examples/for-learning-and-testing/website) examples as
dependencies to show how one boilerplate template can pull in another. It also defines all the variables needed for both
of those dependencies at the top level to show how variable inheritance works.
6 changes: 6 additions & 0 deletions examples/for-learning-and-testing/dependencies/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# {{ .Title }}

{{ .Description }}. It specifies both the
[docs](/examples/for-learning-and-testing/docs) and [website](/examples/for-learning-and-testing/website) examples as
dependencies to show how one boilerplate template can pull in another. It also defines all the variables needed for both
of those dependencies at the top level to show how variable inheritance works.
1 change: 1 addition & 0 deletions examples/for-learning-and-testing/include/target.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ include "../../../test-fixtures/include-test/template.txt" . }}
2 changes: 2 additions & 0 deletions examples/for-learning-and-testing/partials/boilerplate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
partials:
- ../../../test-fixtures/partials-test/*.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Terragrunt Architecture Catalog Boilerplate Example

This folder contains a production level example on how to use `boilerplate` to organize a Terragrunt Architecture. The
concepts in this example is used to construct the Gruntwork Architecture Catalog, which is in turn used to generate the
[Gruntwork Reference Architecture](https://gruntwork.io/reference-architecture/).


## Quickstart

You can try running this example with the following steps:

- Make sure you have `boilerplate` version `v0.4.0` and above.

- Update the [sample_reference_architecture_vars.yml](./sample_reference_architecture_vars.yml) as necessary.

- Run `boilerplate` to invoke the `reference-architecture` blueprint with the given vars:

boilerplate --template-url ./blueprints/reference-architecture --var-file ./sample_reference_architecture_vars.yml --output-folder ./infrastructure-live --non-interactive

You should now have a `infrastructure-live` folder that contains a full `terragrunt` example!


## How to navigate this example

The code in this example is organized into two primary folders:

1. `blueprints`: The consumable architecture blueprints for managing infrastructure components. All the Blueprints that
you will use and invoke are defined within this folder. Refer to the [Terminology section](#terminology) below for
more information on Blueprints.

1. `templates`: The core implementation code of this repo. All the Blueprints wrap and invoke the Templates to generate
the infrastructure code for the underlying components into the various accounts. These are generally too low level to
use directly, and are most useful when constructing Blueprints of your own (e.g., to create an internal Architecture
Catalog for your users). Templates provide a useful building block to share logic across Blueprints. That is, each
Template is potentially used by multiple Blueprints. Templates are further divided into the
following categories:

- `layouts`: Reusable pieces of templates that helps keep the template code DRY. These include the boilerplate
content that are repeated across all the templates.

- `_root`: Templates that render the root contents of a specific folder. For example, the `_root/infrastructure-live`
template renders the content that should exist in the root folder of an `infrastructure-live` repository for
managing live infrastructure config. This includes the root Terragrunt configuration that configures the remote
state.

- `services`: Templates in this directory manage a single piece of infrastructure in a larger architecture. These
are the core building blocks of Blueprints.


## Terminology

This example breaks down `boilerplate` templates into two categories:

* **Template**: Reusable code to generate infrastructure code to deploy and manage one piece of infrastructure in a
single account/deployment. Since Templates only focus on a single piece of infrastructure in a single
account/deployment, invoking a single template does not give you a full deployment for that infrastructure component.
Instead, you need to combine the template with its dependencies, and invoke it for each account that needs the
specific piece of infrastructure. For example, the infrastructure code for an EKS architecture is broken down into four
templates: the VPC, the EKS control plane and worker nodes, the core Kubernetes administrative services, and the
Kubernetes applications. Under the hood, each template uses a single service module from the [Gruntwork Service
Catalog](https://github.com/gruntwork-io/terraform-aws-service-catalog/). To get a full EKS architecture, you need to
deploy all 4 templates in each environment where you wish to run EKS. E.g., You might need to deploy all 4 templates
in dev, then use all 4 again in stage, and then use all 4 once more in prod. To make it easier to use multiple
templates across multiple environments, the Architecture Catalog includes blueprints, as described next.

* **Blueprint**: Reusable code that combines multiple templates together to deploy a complete, self-contained piece of
infrastructure across multiple environments. For example, the EKS blueprint will configure everything you need to
deploy EKS into a multi-account AWS infrastructure setup. This includes the VPC, the EKS control plane and worker
nodes, the core Kubernetes administrative services, and the Kubernetes applications, configured for each account that
needs the infrastructure replicated. Note that Blueprints, like Templates, provide you configuration options for
customizing the behavior of the underlying architecture. The EKS blueprint in the example above gives you
configuration options to skip rendering the VPC template if you already have a custom VPC deployed in your existing
architecture.

This organization exists to streamline the conditional logic of whether or not to include various components in your
architecture. Although `boilerplate` supports replicating nested folders within a template, it does not include the
ability to condition the replication. This makes it hard to implement architecture level logic, like "choose a
monitoring framework," where you want to include different modules based on selection.

This is where the **Blueprints** come in handy, where you can use `skip` directives to control which **Templates** are
invoked.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# This blueprint creates the terragrunt modules necessary to deploy Postgres RDS Cluster into a single region in a
# single account.

variables:
# Required variables
- name: AWSAccountName
description: The name of the AWS account.

- name: AWSRegion
description: The name of the AWS region.

- name: DBSecretsManagerArn
description: "Enter the ID (name or ARN) of an AWS Secrets Manager secret containing the database configuration details."
# Example JSON value for the Secrets Manager secret:
# {
# "engine": "postgres",
# "username": "example-user",
# "password": "example-password",
# "dbname": "myDatabase",
# "port": "3306"
# }

# Optional variables
- name: PostgresEngineVersion
type: string
description: "The version of Postgres to use. For allowed values, see the AWS documentation here: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.DBVersions"
default: "14.1"

- name: IncludeVPCApp
type: bool
description: Whether or not to render the vpc template as a dependency.
default: true

# The following are convenience variables for DRY-ing up the dependencies. These are not intended to be
# updated/provided by users. We follow the Go convention of camelCase instead of TitleCase.
- name: accountOutputFolder
description: "The output folder that denotes the account in the infrastructure-live folder structure."
default: "{{ outputFolder }}/{{ .AWSAccountName }}/"
- name: accountRegionOutputFolder
description: "The output folder that denotes the region in the infrastructure-live folder structure."
default: "{{ .accountOutputFolder }}/{{ .AWSRegion }}/"

dependencies:
- name: rds
template-url: "{{ templateFolder }}/../../templates/services/data-stores/rds"
output-folder: "{{ .accountRegionOutputFolder }}/data-stores/postgres"
variables:
- name: IsEnvCommon
type: bool
default: false
- name: RDSEngine
type: string
default: "postgres"
- name: RDSEngineVersion
type: string
default: "{{ .PostgresEngineVersion }}"

- name: rds-envcommon
template-url: "{{ templateFolder }}/../../templates/services/data-stores/rds"
output-folder: "{{ outputFolder }}/_envcommon/data-stores"
variables:
- name: IsEnvCommon
type: bool
default: true
- name: RDSEngine
type: string
default: "postgres"
- name: RDSEngineVersion
type: string
default: "{{ .PostgresEngineVersion }}"

# The following are additional blueprints to include. We use blueprints instead of templates for these dependencies so
# that we can include everything necessary for each component.
- name: vpc-app
template-url: "{{ templateFolder }}/../vpc-app"
output-folder: "{{ outputFolder }}"
skip: "{{ not .IncludeVPCApp }}"


hooks:
after:
# Format the rendered HCL files
- command: terragrunt
args:
- "hclfmt"
- "--terragrunt-working-dir"
- "{{ outputFolder }}"
Loading