Skip to content

Commit ccb0fbd

Browse files
authored
Merge pull request #59 from gruntwork-io/inherit
Support inheritance and interpolation in .terragrunt files
2 parents b87cf01 + fe2077a commit ccb0fbd

19 files changed

Lines changed: 1369 additions & 117 deletions

File tree

README.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,220 @@ remote_state = {
242242
different key/value pairs, so consult the [Terraform remote state docs](https://www.terraform.io/docs/state/remote/)
243243
for details.
244244

245+
## Managing multiple .terragrunt files
246+
247+
With Terraform, it can be a good idea to store your templates in separate folders (and therefore, separate state files)
248+
to provide isolation between different environments,such as stage and prod, and different components, such as a
249+
database and an app cluster (for more info, see [How to Manage Terraform
250+
State](https://blog.gruntwork.io/how-to-manage-terraform-state-28f5697e68fa)). That means you will need a `.terragrunt`
251+
file in each folder:
252+
253+
```
254+
my-terraform-repo
255+
└ qa
256+
└ my-app
257+
└ main.tf
258+
└ .terragrunt
259+
└ stage
260+
└ my-app
261+
└ main.tf
262+
└ .terragrunt
263+
└ prod
264+
└ my-app
265+
└ main.tf
266+
└ .terragrunt
267+
```
268+
269+
Most of these `.terragrunt` files will have the almost the same content. For example, `qa/my-app/.terragrunt` may look
270+
like this:
271+
272+
```hcl
273+
# Configure Terragrunt to use DynamoDB for locking
274+
lock = {
275+
backend = "dynamodb"
276+
config {
277+
state_file_id = "qa/my-app"
278+
}
279+
}
280+
281+
# Configure Terragrunt to automatically store tfstate files in an S3 bucket
282+
remote_state = {
283+
backend = "s3"
284+
config {
285+
encrypt = "true"
286+
bucket = "my-bucket"
287+
key = "qa/my-app/terraform.tfstate"
288+
region = "us-east-1"
289+
}
290+
}
291+
```
292+
293+
And `stage/my-app/.terragrunt` may look like this:
294+
295+
```hcl
296+
# Configure Terragrunt to use DynamoDB for locking
297+
lock = {
298+
backend = "dynamodb"
299+
config {
300+
state_file_id = "stage/my-app"
301+
}
302+
}
303+
304+
# Configure Terragrunt to automatically store tfstate files in an S3 bucket
305+
remote_state = {
306+
backend = "s3"
307+
config {
308+
encrypt = "true"
309+
bucket = "my-bucket"
310+
key = "stage/my-app/terraform.tfstate"
311+
region = "us-east-1"
312+
}
313+
}
314+
```
315+
316+
Note how most of the content is copy/pasted, except for the `state_file_id` and `key` parameters, which match the path
317+
of the `.terragrunt` file itself. How do you avoid having to manually maintain the contents of all of these
318+
similar-looking `.terragrunt` files?
319+
320+
The solution is to use the following features of Terragrunt:
321+
322+
* Includes
323+
* Find parent helper
324+
* Relative path helper
325+
* Overriding included settings
326+
327+
### Includes
328+
329+
One `.terragrunt` file can automatically "include" the contents of another `.terragrunt` file using the `include`
330+
block. For example, imagine you have the following file layout:
331+
332+
```
333+
my-terraform-repo
334+
└ .terragrunt
335+
└ qa
336+
└ my-app
337+
└ main.tf
338+
└ .terragrunt
339+
└ stage
340+
└ my-app
341+
└ main.tf
342+
└ .terragrunt
343+
└ prod
344+
└ my-app
345+
└ main.tf
346+
└ .terragrunt
347+
```
348+
349+
The `.terragrunt` file in the root folder defines the typical `lock` and `remote_state` settings. The `.terragrunt`
350+
files in all the subfolders (e.g. `qa/my-app/.terragrunt`) can automatically include all the settings from a parent
351+
file using the `include` block:
352+
353+
```hcl
354+
include = {
355+
path = "../../.terragrunt"
356+
}
357+
```
358+
359+
When you run Terragrunt in the `qa/my-app` folder, it will see the `include` block in the `qa/my-app/.terragrunt` file
360+
and realize that it should load the contents of the root `.terragrunt` file instead. It's almost as if you had
361+
copy/pasted the contents of the root `.terragrunt` file into `qa/my-app/.terragrunt`, but much easier to maintain!
362+
363+
**Note**: only one level of includes is allowed. If `root/qa/my-app/.terragrunt` includes `root/.terragrunt`, then
364+
`root/.terragrunt` may NOT specify an `include` block.
365+
366+
There are a few problems with the simple approach above, so read on before using it!
367+
368+
1. Having to manually manage the file paths to the included `.terragrunt` file is tedious and error prone. To solve
369+
this problem, you can use the `find_in_parent_folders()` helper.
370+
1. If the included `.terragrunt` file hard-codes the `state_file_id` and `key` settings, then every child that includes
371+
it would end up using the same lock and write state to the same location. To avoid this problem, you can use the
372+
`path_relative_to_include()` helper.
373+
1. Some of the child `.terragrunt` files may want to override the settings they include. To do this, see the section
374+
on overriding included settings.
375+
376+
Each of these items is discussed next.
377+
378+
### find_in_parent_folders helper
379+
380+
Terragrunt supports the use of a few helper functions using the same syntax as Terraform: `${some_function()}`. One of
381+
the supported helper functions is `find_in_parent_folders()`, which returns the path to the first `.terragrunt` file it
382+
finds in the parent folders above the current `.terragrunt` file.
383+
384+
Example:
385+
386+
```hcl
387+
include = {
388+
path = "${find_in_parent_folders()}"
389+
}
390+
```
391+
392+
If you ran this in `qa/my-app/.terragrunt`, this would automatically set `path` to `../../.terragrunt`. You will almost
393+
always want to use this function, as it allows you to copy/paste the same `.terragrunt` file to all child folders with
394+
no changes.
395+
396+
`find_in_parent_folders()` will search up the directory tree until it hits the root folder of your file system, and if
397+
no `.terragrunt` file is found, Terragrunt will exit with an error.
398+
399+
### path_relative_to_include helper
400+
401+
Another helper function supported by Terragrunt is `path_relative_to_include()`, which returns the relative path between
402+
the current `.terragrunt` file and the path specified in its `include` block. For example, in the root `.terragrunt`
403+
file, you could do the following:
404+
405+
```hcl
406+
# Configure Terragrunt to use DynamoDB for locking
407+
lock = {
408+
backend = "dynamodb"
409+
config {
410+
state_file_id = "${path_relative_to_include()}"
411+
}
412+
}
413+
414+
# Configure Terragrunt to automatically store tfstate files in an S3 bucket
415+
remote_state = {
416+
backend = "s3"
417+
config {
418+
encrypt = "true"
419+
bucket = "my-bucket"
420+
key = "${path_relative_to_include()}/terraform.tfstate"
421+
region = "us-east-1"
422+
}
423+
}
424+
```
425+
426+
Each child `.terragrunt` file that references the configuration above in its `include` block will get a unique path for
427+
its `state_file_id` and `key` settings. For example, in `qa/my-app/.terragrunt`, the `state_file_id` will resolve to
428+
`qa/my-app` and the `key` will resolve to `qa/my-app/terraform.tfstate`.
429+
430+
You will almost always want to use this helper too. The only time you may want to specify the `state_file_id` or `key`
431+
manually is if you moved a child folder. In that case, to ensure it can reuse its old state and lock, you may want to
432+
hard-code the `state_file_id` and `key` to the old file path. However, a safer approach would be to move the state
433+
files themselves to match the new location of the child folder, as that makes things more consistent!
434+
435+
### Overriding included settings
436+
437+
Any settings in the child `.terragrunt` file will override the settings pulled in via an `include`. For example,
438+
imagine if `qa/my-app/.terragrunt` had the following contents:
439+
440+
```hcl
441+
include = {
442+
path = "${find_in_parent_folders()}"
443+
}
444+
445+
remote_state = {
446+
backend = "s3"
447+
config {
448+
encrypt = "true"
449+
bucket = "some-other-bucket"
450+
key = "/foo/bar/terraform.tfstate"
451+
region = "us-west-2"
452+
}
453+
}
454+
```
455+
456+
The result is that when you run `terragrunt` commands in the `qa/my-app` folder, you get the `lock` settings from the
457+
parent, but the `remote_state` settings of the child.
458+
245459
## CLI Options
246460

247461
Terragrunt forwards all arguments and options to Terraform. The only exceptions are the options that start with the

0 commit comments

Comments
 (0)