From 49121c63120d3406774ee4c7a38708441fb391d8 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 14 Sep 2021 16:00:45 +0300 Subject: [PATCH 01/18] IamRole decode in base blocks --- config/config_partial.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/config/config_partial.go b/config/config_partial.go index c269991060..783cca721f 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -2,11 +2,10 @@ package config import ( "fmt" - "path/filepath" - "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" "github.com/zclconf/go-cty/cty" + "path/filepath" "github.com/gruntwork-io/terragrunt/errors" "github.com/gruntwork-io/terragrunt/options" @@ -99,6 +98,16 @@ func DecodeBaseBlocks( filename string, includeFromChild *IncludeConfig, ) (*cty.Value, *terragruntInclude, TrackInclude, error) { + // first decode of IamRole which can be referenced in other blocks + contextExtensions := EvalContextExtensions{} + flags := terragruntFlags{} + err := decodeHcl(hclFile, filename, &flags, terragruntOptions, contextExtensions) + if err != nil { + return nil, nil, TrackInclude{}, err + } + if flags.IamRole != nil { + terragruntOptions.IamRole = *flags.IamRole + } // Decode just the `include` block, and verify that it's allowed here terragruntInclude, err := decodeAsTerragruntInclude( hclFile, From cd9c5ef4cd1df8dcfa66112eff9e15059977835f Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 14 Sep 2021 16:01:34 +0300 Subject: [PATCH 02/18] Cleanup --- config/config_partial.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/config_partial.go b/config/config_partial.go index 783cca721f..4a5e821749 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -2,10 +2,11 @@ package config import ( "fmt" + "path/filepath" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" "github.com/zclconf/go-cty/cty" - "path/filepath" "github.com/gruntwork-io/terragrunt/errors" "github.com/gruntwork-io/terragrunt/options" From 45661495a9079d41a161a730d2f16bdeee292f62 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 14 Sep 2021 17:59:55 +0300 Subject: [PATCH 03/18] Test for loading IAM role from file --- .../iam_role_in_file/main.tf | 1 + .../iam_role_in_file/terragrunt.hcl | 5 +++ test/integration_test.go | 38 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 test/fixture-read-config/iam_role_in_file/main.tf create mode 100644 test/fixture-read-config/iam_role_in_file/terragrunt.hcl diff --git a/test/fixture-read-config/iam_role_in_file/main.tf b/test/fixture-read-config/iam_role_in_file/main.tf new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test/fixture-read-config/iam_role_in_file/main.tf @@ -0,0 +1 @@ + diff --git a/test/fixture-read-config/iam_role_in_file/terragrunt.hcl b/test/fixture-read-config/iam_role_in_file/terragrunt.hcl new file mode 100644 index 0000000000..d1d9450b42 --- /dev/null +++ b/test/fixture-read-config/iam_role_in_file/terragrunt.hcl @@ -0,0 +1,5 @@ +iam_role = "__IAM_PLACEHOLDER__" + +locals { + iam_text = run_cmd("echo", "${get_aws_account_id()}") +} diff --git a/test/integration_test.go b/test/integration_test.go index 4e7b8ad841..107cd4e7ad 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -118,6 +118,7 @@ const ( TEST_FIXTURE_LOCAL_RUN_MULTIPLE = "fixture-locals/run-multiple" TEST_FIXTURE_LOCALS_IN_INCLUDE_CHILD_REL_PATH = "qa/my-app" TEST_FIXTURE_READ_CONFIG = "fixture-read-config" + TEST_FIXTURE_READ_IAM_ROLE = "fixture-read-config/iam_role_in_file" TEST_FIXTURE_AWS_GET_CALLER_IDENTITY = "fixture-get-aws-caller-identity" TEST_FIXTURE_GET_PLATFORM = "fixture-get-platform" TEST_FIXTURE_GET_TERRAGRUNT_SOURCE_HCL = "fixture-get-terragrunt-source-hcl" @@ -3589,6 +3590,43 @@ func TestTerragruntVersionConstraints(t *testing.T) { } } +func TestReadTerragruntConfigIamRole(t *testing.T) { + t.Parallel() + + identityArn, err := aws_helper.GetAWSIdentityArn(nil, &options.TerragruntOptions { + IamRole: "", + }) + assert.NoError(t, err) + + tmpEnvPath := copyEnvironment(t, TEST_FIXTURE_READ_IAM_ROLE) + rootPath := util.JoinPath(tmpEnvPath, TEST_FIXTURE_READ_IAM_ROLE) + + // Replace current IAM role in test file + hclFile := util.JoinPath(rootPath, config.DefaultTerragruntConfigPath) + contents, err := util.ReadFileAsString(hclFile) + if err != nil { + t.Fatalf("Error reading Terragrunt config at %s: %v", hclFile, err) + } + contents = strings.Replace(contents, "__IAM_PLACEHOLDER__", identityArn, -1) + if err := ioutil.WriteFile(hclFile, []byte(contents), 0444); err != nil { + t.Fatalf("Error writing temp Terragrunt config to %s: %v", hclFile, err) + } + + // execution outputs to be verified + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + + // run terragrunt with explicit `terragrunt-iam-role` + err = runTerragruntCommand(t, fmt.Sprintf("terragrunt init --terragrunt-iam-role arn:aws:iam::1111111111:role/admin --terragrunt-working-dir %s", rootPath), &stdout, &stderr) + + // since are used not existing AWS accounts, for validation are used success and error outputs + output := fmt.Sprintf("%v %v %v", string(stderr.Bytes()), string(stdout.Bytes()), err.Error()) + + // Check that output doesn't contain passed as argument IAM role, and value from file is referenced + assert.Equal(t, 0, strings.Count(output, "1111111111")) + assert.NotEqual(t, 0, strings.Count(output, identityArn)) +} + func TestTerragruntVersionConstraintsPartialParse(t *testing.T) { fixturePath := "fixture-partial-parse/terragrunt-version-constraint" cleanupTerragruntFolder(t, fixturePath) From 60b8f9184018bf200e20b9ffcb65cf7cc013ea99 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 14 Sep 2021 18:02:50 +0300 Subject: [PATCH 04/18] Integration tests cleanup --- test/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration_test.go b/test/integration_test.go index 107cd4e7ad..da50749756 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -3593,7 +3593,7 @@ func TestTerragruntVersionConstraints(t *testing.T) { func TestReadTerragruntConfigIamRole(t *testing.T) { t.Parallel() - identityArn, err := aws_helper.GetAWSIdentityArn(nil, &options.TerragruntOptions { + identityArn, err := aws_helper.GetAWSIdentityArn(nil, &options.TerragruntOptions{ IamRole: "", }) assert.NoError(t, err) From 870009cc89fee86921df5de5e2fb530dae28ab8f Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 14 Sep 2021 18:11:20 +0300 Subject: [PATCH 05/18] Updated description --- test/integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration_test.go b/test/integration_test.go index da50749756..0064214e71 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -3616,13 +3616,13 @@ func TestReadTerragruntConfigIamRole(t *testing.T) { stdout := bytes.Buffer{} stderr := bytes.Buffer{} - // run terragrunt with explicit `terragrunt-iam-role` + // explicit run with --terragrunt-iam-role err = runTerragruntCommand(t, fmt.Sprintf("terragrunt init --terragrunt-iam-role arn:aws:iam::1111111111:role/admin --terragrunt-working-dir %s", rootPath), &stdout, &stderr) // since are used not existing AWS accounts, for validation are used success and error outputs output := fmt.Sprintf("%v %v %v", string(stderr.Bytes()), string(stdout.Bytes()), err.Error()) - // Check that output doesn't contain passed as argument IAM role, and value from file is referenced + // Check that output doesn't contain passed as argument IAM role, and value identityArn is referenced assert.Equal(t, 0, strings.Count(output, "1111111111")) assert.NotEqual(t, 0, strings.Count(output, identityArn)) } From fcbe18589de4c188ba1835c5778b1dd2e256fbce Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 15 Sep 2021 10:06:20 +0300 Subject: [PATCH 06/18] Upstream merge Simplified test for get_aws_account_id --- config/config_partial.go | 18 +++++++----- .../iam_role_in_file/main.tf | 4 ++- .../iam_role_in_file/terragrunt.hcl | 14 +++++++-- test/integration_test.go | 29 ++++++------------- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/config/config_partial.go b/config/config_partial.go index dbcb5328c4..2eb32de2e1 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -102,14 +102,16 @@ func DecodeBaseBlocks( ) (*cty.Value, *TrackInclude, error) { // first decode of IamRole which can be referenced in other blocks - contextExtensions := EvalContextExtensions{} - flags := terragruntFlags{} - err := decodeHcl(hclFile, filename, &flags, terragruntOptions, contextExtensions) - if err != nil { - return nil, nil, TrackInclude{}, err - } - if flags.IamRole != nil { - terragruntOptions.IamRole = *flags.IamRole + if terragruntOptions.IamRole == "" { + contextExtensions := EvalContextExtensions{} + flags := terragruntFlags{} + err := decodeHcl(hclFile, filename, &flags, terragruntOptions, contextExtensions) + if err != nil { + return nil, nil, err + } + if flags.IamRole != nil { + terragruntOptions.IamRole = *flags.IamRole + } } // Decode just the `include` and `import` blocks, and verify that it's allowed here diff --git a/test/fixture-read-config/iam_role_in_file/main.tf b/test/fixture-read-config/iam_role_in_file/main.tf index 8b13789179..f966bbb901 100644 --- a/test/fixture-read-config/iam_role_in_file/main.tf +++ b/test/fixture-read-config/iam_role_in_file/main.tf @@ -1 +1,3 @@ - +terraform { + backend "local" {} +} diff --git a/test/fixture-read-config/iam_role_in_file/terragrunt.hcl b/test/fixture-read-config/iam_role_in_file/terragrunt.hcl index d1d9450b42..d43e3ebd68 100644 --- a/test/fixture-read-config/iam_role_in_file/terragrunt.hcl +++ b/test/fixture-read-config/iam_role_in_file/terragrunt.hcl @@ -1,5 +1,13 @@ -iam_role = "__IAM_PLACEHOLDER__" +iam_role = "arn:aws:iam::666666666666:role/terragrunttest" -locals { - iam_text = run_cmd("echo", "${get_aws_account_id()}") +remote_state { + backend = "local" + generate = { + // state file should load value from iam_role + path = "${get_aws_account_id()}.txt" + if_exists = "overwrite" + } + config = { + path = "terraform.tfstate" + } } diff --git a/test/integration_test.go b/test/integration_test.go index 3f59205c3a..88a8029245 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -3630,33 +3630,22 @@ func TestReadTerragruntConfigIamRole(t *testing.T) { }) assert.NoError(t, err) - tmpEnvPath := copyEnvironment(t, TEST_FIXTURE_READ_IAM_ROLE) - rootPath := util.JoinPath(tmpEnvPath, TEST_FIXTURE_READ_IAM_ROLE) + cleanupTerraformFolder(t, TEST_FIXTURE_READ_IAM_ROLE) - // Replace current IAM role in test file - hclFile := util.JoinPath(rootPath, config.DefaultTerragruntConfigPath) - contents, err := util.ReadFileAsString(hclFile) - if err != nil { - t.Fatalf("Error reading Terragrunt config at %s: %v", hclFile, err) - } - contents = strings.Replace(contents, "__IAM_PLACEHOLDER__", identityArn, -1) - if err := ioutil.WriteFile(hclFile, []byte(contents), 0444); err != nil { - t.Fatalf("Error writing temp Terragrunt config to %s: %v", hclFile, err) - } - - // execution outputs to be verified + // Execution outputs to be verified stdout := bytes.Buffer{} stderr := bytes.Buffer{} - // explicit run with --terragrunt-iam-role - err = runTerragruntCommand(t, fmt.Sprintf("terragrunt init --terragrunt-iam-role arn:aws:iam::1111111111:role/admin --terragrunt-working-dir %s", rootPath), &stdout, &stderr) + // Invoke terragrunt and verify used IAM role + err = runTerragruntCommand(t, fmt.Sprintf("terragrunt init --terragrunt-working-dir %s", TEST_FIXTURE_READ_IAM_ROLE), &stdout, &stderr) - // since are used not existing AWS accounts, for validation are used success and error outputs + // Since are used not existing AWS accounts, for validation are used success and error outputs output := fmt.Sprintf("%v %v %v", string(stderr.Bytes()), string(stdout.Bytes()), err.Error()) - // Check that output doesn't contain passed as argument IAM role, and value identityArn is referenced - assert.Equal(t, 0, strings.Count(output, "1111111111")) - assert.NotEqual(t, 0, strings.Count(output, identityArn)) + // Check that output contains value defined in IAM role + assert.Equal(t, 1, strings.Count(output, "666666666666")) + // Ensure that state file wasn't created with default IAM value + assert.True(t, util.FileNotExists(util.JoinPath(TEST_FIXTURE_READ_IAM_ROLE, identityArn+".txt"))) } func TestTerragruntVersionConstraintsPartialParse(t *testing.T) { From 61f9bf65fa04491808e85adf707381d1f5b88136 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 15 Sep 2021 11:08:08 +0300 Subject: [PATCH 07/18] IamRole loading during parsing of config files --- config/config.go | 11 +++++++++++ config/config_partial.go | 13 ------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/config/config.go b/config/config.go index f2ac036653..9678209440 100644 --- a/config/config.go +++ b/config/config.go @@ -545,6 +545,17 @@ func ParseConfigFile(filename string, terragruntOptions *options.TerragruntOptio return nil, err } + // for config file and empty IamRole try to load IamConfig first + if include == nil && terragruntOptions.IamRole == "" { + iamConfig, err := PartialParseConfigString(configString, terragruntOptions, include, filename, []PartialDecodeSectionType{TerragruntFlags}) + if err != nil { + return nil, err + } + if iamConfig.IamRole != "" { + terragruntOptions.IamRole = iamConfig.IamRole + } + } + config, err := ParseConfigString(configString, terragruntOptions, include, filename, dependencyOutputs) if err != nil { return nil, err diff --git a/config/config_partial.go b/config/config_partial.go index 2eb32de2e1..361286e58d 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -101,19 +101,6 @@ func DecodeBaseBlocks( decodeList []PartialDecodeSectionType, ) (*cty.Value, *TrackInclude, error) { - // first decode of IamRole which can be referenced in other blocks - if terragruntOptions.IamRole == "" { - contextExtensions := EvalContextExtensions{} - flags := terragruntFlags{} - err := decodeHcl(hclFile, filename, &flags, terragruntOptions, contextExtensions) - if err != nil { - return nil, nil, err - } - if flags.IamRole != nil { - terragruntOptions.IamRole = *flags.IamRole - } - } - // Decode just the `include` and `import` blocks, and verify that it's allowed here terragruntIncludeList, err := decodeAsTerragruntInclude( hclFile, From 88d2f587f57c59934c6fc1155519816bbf0d3ae9 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 15 Sep 2021 11:15:57 +0300 Subject: [PATCH 08/18] Updated description --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 9678209440..221a92c294 100644 --- a/config/config.go +++ b/config/config.go @@ -545,7 +545,7 @@ func ParseConfigFile(filename string, terragruntOptions *options.TerragruntOptio return nil, err } - // for config file and empty IamRole try to load IamConfig first + // If IamRole wasn't provided, and if it is config file, try first to decode IamRole if include == nil && terragruntOptions.IamRole == "" { iamConfig, err := PartialParseConfigString(configString, terragruntOptions, include, filename, []PartialDecodeSectionType{TerragruntFlags}) if err != nil { From 756cce8ffa8e9bc3935ba5c0ba0ac5a12adeeca6 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 15 Sep 2021 11:25:47 +0300 Subject: [PATCH 09/18] Updated test assertions since locals +1 time --- test/integration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration_test.go b/test/integration_test.go index 88a8029245..8afa0b0096 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -4329,7 +4329,7 @@ func TestTerragruntInitRunCmd(t *testing.T) { assert.Equal(t, 1, strings.Count(errout, "input_variable")) // Commands executed multiple times because of different arguments - assert.Equal(t, 3, strings.Count(errout, "uuid")) - assert.Equal(t, 4, strings.Count(errout, "random_arg")) - assert.Equal(t, 3, strings.Count(errout, "another_arg")) + assert.Equal(t, 4, strings.Count(errout, "uuid")) + assert.Equal(t, 6, strings.Count(errout, "random_arg")) + assert.Equal(t, 4, strings.Count(errout, "another_arg")) } From 606612f4813fa03762e540016dd016baa2c7b55d Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 15 Sep 2021 15:51:47 +0300 Subject: [PATCH 10/18] Added reference to issue name --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 221a92c294..6e5ad66d62 100644 --- a/config/config.go +++ b/config/config.go @@ -546,6 +546,7 @@ func ParseConfigFile(filename string, terragruntOptions *options.TerragruntOptio } // If IamRole wasn't provided, and if it is config file, try first to decode IamRole + // https://github.com/gruntwork-io/terragrunt/issues/667 if include == nil && terragruntOptions.IamRole == "" { iamConfig, err := PartialParseConfigString(configString, terragruntOptions, include, filename, []PartialDecodeSectionType{TerragruntFlags}) if err != nil { From 0eb1cb55803d2c9aec53d0c193c30a660b3c62f1 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Thu, 16 Sep 2021 10:07:46 +0300 Subject: [PATCH 11/18] Description update --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 6e5ad66d62..d1416c41ef 100644 --- a/config/config.go +++ b/config/config.go @@ -545,7 +545,7 @@ func ParseConfigFile(filename string, terragruntOptions *options.TerragruntOptio return nil, err } - // If IamRole wasn't provided, and if it is config file, try first to decode IamRole + // Initial evaluation of configuration to load flags like IamRole which will be used for final parsing // https://github.com/gruntwork-io/terragrunt/issues/667 if include == nil && terragruntOptions.IamRole == "" { iamConfig, err := PartialParseConfigString(configString, terragruntOptions, include, filename, []PartialDecodeSectionType{TerragruntFlags}) From b57bf54a0c1be892d872bda30665ac064e0d608a Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Thu, 16 Sep 2021 10:16:56 +0300 Subject: [PATCH 12/18] Documentation update --- docs/_docs/04_reference/built-in-functions.md | 7 +++++-- docs/_docs/04_reference/config-blocks-and-attributes.md | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/_docs/04_reference/built-in-functions.md b/docs/_docs/04_reference/built-in-functions.md index 3817941956..c91f8c73ab 100644 --- a/docs/_docs/04_reference/built-in-functions.md +++ b/docs/_docs/04_reference/built-in-functions.md @@ -591,6 +591,7 @@ Output: $ terragrunt init uuid1 b48379e1-924d-2403-8789-c72d50be964c uuid1 9f3a8398-b11f-5314-7783-dad176ee487d +uuid1 649ac501-e5db-c935-1499-c59fb7a75625 uuid2 2d65972b-3fa9-181f-64fe-dcd574d944d0 uuid3 e345de60-9cfa-0455-79b7-af0d053a15a5 potato @@ -598,12 +599,14 @@ uuid3 7f90a4ed-96e3-1dd8-5fee-91b8c8e07650 uuid2 8638fe79-c589-bebd-2a2a-3e6b96f7fc34 uuid3 310d0447-f0a6-3f67-efda-e6b1521fa1fb uuid4 f8e80cc6-1892-8db7-bd63-6089fef00c01 +uuid2 289ff371-8021-54c6-2254-72de9d11392a +uuid3 baa19863-1d99-e0ef-11f2-ede830d1c58a carrot ``` **Notes:** * Output contains only once `carrot` and `potato`, because other invocations got cached, caching works for all sections - * Output contains twice `uuid1` and `uuid2` because during HCL evaluation each `run_cmd` in `locals` section is evaluated twice, and value is cached under different key since `uuid()` add random value in key - * Output contains three times `uuid3` - 2 prints because `uuid3` was declared in `locals`, once because it is declared in `inputs` + * Output contains multiple times `uuid1` and `uuid2` because during HCL evaluation each `run_cmd` in `locals` is evaluated multiple times and random argument generated from `uuid()` save cached value under different key each time + * Output contains multiple times `uuid3`, +1 more output comparing to `uuid1` and `uuid2` - because `uuid3` is declared in locals and inputs which add one more evaluation * Output contains only once `uuid4` since it is declared only once in `inputs`, `inputs` is not evaluated twice ## read\_terragrunt\_config diff --git a/docs/_docs/04_reference/config-blocks-and-attributes.md b/docs/_docs/04_reference/config-blocks-and-attributes.md index 0eda7d4a47..5939ff08cf 100644 --- a/docs/_docs/04_reference/config-blocks-and-attributes.md +++ b/docs/_docs/04_reference/config-blocks-and-attributes.md @@ -970,7 +970,9 @@ Example: ```hcl iam_role = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME" ``` - +**Notes:** + * Definitions of `iam_role` can be included from other HCL files + * Value of `iam_role` can reference locals variable ### iam_assume_role_duration From 728a7d2590427b59cac86fd84d06b54f56ac0c65 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Tue, 28 Sep 2021 23:59:23 +0300 Subject: [PATCH 13/18] IamConfig parsing updates --- config/config.go | 27 ++++++++++--------- config/config_partial.go | 1 - .../config-blocks-and-attributes.md | 4 +-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/config/config.go b/config/config.go index caa59524a4..af4eecf7ee 100644 --- a/config/config.go +++ b/config/config.go @@ -551,18 +551,6 @@ func ParseConfigFile(filename string, terragruntOptions *options.TerragruntOptio return nil, err } - // Initial evaluation of configuration to load flags like IamRole which will be used for final parsing - // https://github.com/gruntwork-io/terragrunt/issues/667 - if include == nil && terragruntOptions.IamRole == "" { - iamConfig, err := PartialParseConfigString(configString, terragruntOptions, include, filename, []PartialDecodeSectionType{TerragruntFlags}) - if err != nil { - return nil, err - } - if iamConfig.IamRole != "" { - terragruntOptions.IamRole = iamConfig.IamRole - } - } - config, err := ParseConfigString(configString, terragruntOptions, include, filename, dependencyOutputs) if err != nil { return nil, err @@ -611,6 +599,21 @@ func ParseConfigString( return nil, err } + // Initial evaluation of configuration to load flags like IamRole which will be used for final parsing + // https://github.com/gruntwork-io/terragrunt/issues/667 + if terragruntOptions.IamRole == "" { + iamConfig, err := PartialParseConfigString(configString, terragruntOptions, nil, filename, []PartialDecodeSectionType{TerragruntFlags}) + if err != nil { + return nil, err + } + if iamConfig.IamRole != "" { + terragruntOptions.IamRole = iamConfig.IamRole + } + if iamConfig.IamAssumeRoleDuration != nil { + terragruntOptions.IamAssumeRoleDuration = *iamConfig.IamAssumeRoleDuration + } + } + // Decode just the Base blocks. See the function docs for DecodeBaseBlocks for more info on what base blocks are. localsAsCty, trackInclude, err := DecodeBaseBlocks(terragruntOptions, parser, file, filename, includeFromChild, nil) if err != nil { diff --git a/config/config_partial.go b/config/config_partial.go index 361286e58d..c16bc54a1d 100644 --- a/config/config_partial.go +++ b/config/config_partial.go @@ -100,7 +100,6 @@ func DecodeBaseBlocks( includeFromChild *IncludeConfig, decodeList []PartialDecodeSectionType, ) (*cty.Value, *TrackInclude, error) { - // Decode just the `include` and `import` blocks, and verify that it's allowed here terragruntIncludeList, err := decodeAsTerragruntInclude( hclFile, diff --git a/docs/_docs/04_reference/config-blocks-and-attributes.md b/docs/_docs/04_reference/config-blocks-and-attributes.md index 23cd6f679e..e03679b155 100644 --- a/docs/_docs/04_reference/config-blocks-and-attributes.md +++ b/docs/_docs/04_reference/config-blocks-and-attributes.md @@ -1129,8 +1129,8 @@ Example: iam_role = "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME" ``` **Notes:** - * Definitions of `iam_role` can be included from other HCL files - * Value of `iam_role` can reference locals variable + * Value of `iam_role` can reference local variables + * Definitions of `iam_role` included from other HCL files through `include` ### iam_assume_role_duration From 1a95205c07b925375127e2a808227f57ecd01d18 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 29 Sep 2021 00:07:30 +0300 Subject: [PATCH 14/18] Extracted IAM parsing as separated function --- config/config.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index af4eecf7ee..786e113d1b 100644 --- a/config/config.go +++ b/config/config.go @@ -601,17 +601,8 @@ func ParseConfigString( // Initial evaluation of configuration to load flags like IamRole which will be used for final parsing // https://github.com/gruntwork-io/terragrunt/issues/667 - if terragruntOptions.IamRole == "" { - iamConfig, err := PartialParseConfigString(configString, terragruntOptions, nil, filename, []PartialDecodeSectionType{TerragruntFlags}) - if err != nil { - return nil, err - } - if iamConfig.IamRole != "" { - terragruntOptions.IamRole = iamConfig.IamRole - } - if iamConfig.IamAssumeRoleDuration != nil { - terragruntOptions.IamAssumeRoleDuration = *iamConfig.IamAssumeRoleDuration - } + if err := extractIamRole(configString, terragruntOptions, filename); err != nil { + return nil, err } // Decode just the Base blocks. See the function docs for DecodeBaseBlocks for more info on what base blocks are. @@ -662,6 +653,23 @@ func ParseConfigString( return config, nil } +// extractIamRole - extract IAM role details from Terragrunt flags block +func extractIamRole(configString string, terragruntOptions *options.TerragruntOptions, filename string) error { + if terragruntOptions.IamRole == "" { + iamConfig, err := PartialParseConfigString(configString, terragruntOptions, nil, filename, []PartialDecodeSectionType{TerragruntFlags}) + if err != nil { + return err + } + if iamConfig.IamRole != "" { + terragruntOptions.IamRole = iamConfig.IamRole + } + if iamConfig.IamAssumeRoleDuration != nil { + terragruntOptions.IamAssumeRoleDuration = *iamConfig.IamAssumeRoleDuration + } + } + return nil +} + func decodeAsTerragruntConfigFile( file *hcl.File, filename string, From 118ef94e10ef715e26aba23e94b0cdcfb3041fe9 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 29 Sep 2021 14:16:08 +0300 Subject: [PATCH 15/18] Documentation update --- docs/_docs/01_getting-started/configuration.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/_docs/01_getting-started/configuration.md b/docs/_docs/01_getting-started/configuration.md index 28021043fa..ca0ca963bc 100644 --- a/docs/_docs/01_getting-started/configuration.md +++ b/docs/_docs/01_getting-started/configuration.md @@ -55,15 +55,17 @@ Currently terragrunt parses the config in the following order: 2. `locals` block -3. `dependencies` block +3. Evaluation of values for `iam_role` and `iam_assume_role_duration` attributes, if defined -4. `dependency` blocks, including calling `terragrunt output` on the dependent modules to retrieve the outputs +4. `dependencies` block -5. Everything else +5. `dependency` blocks, including calling `terragrunt output` on the dependent modules to retrieve the outputs -6. The config referenced by `include` +6. Everything else -7. A merge operation between the config referenced by `include` and the current config. +7. The config referenced by `include` + +8. A merge operation between the config referenced by `include` and the current config. Blocks that are parsed earlier in the process will be made available for use in the parsing of later blocks. Similarly, you cannot use blocks that are parsed later earlier in the process (e.g you can’t reference `dependency` in `locals`, `include`, or `dependencies` blocks). From 2db4e5b5983bc121f77a353a0d069e2ba00f78e2 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Wed, 29 Sep 2021 21:28:32 +0300 Subject: [PATCH 16/18] Renamed extractIamRole to setIAMRole --- config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index 786e113d1b..3dc87c2816 100644 --- a/config/config.go +++ b/config/config.go @@ -601,7 +601,7 @@ func ParseConfigString( // Initial evaluation of configuration to load flags like IamRole which will be used for final parsing // https://github.com/gruntwork-io/terragrunt/issues/667 - if err := extractIamRole(configString, terragruntOptions, filename); err != nil { + if err := setIAMRole(configString, terragruntOptions, filename); err != nil { return nil, err } @@ -653,8 +653,8 @@ func ParseConfigString( return config, nil } -// extractIamRole - extract IAM role details from Terragrunt flags block -func extractIamRole(configString string, terragruntOptions *options.TerragruntOptions, filename string) error { +// setIAMRole - extract IAM role details from Terragrunt flags block +func setIAMRole(configString string, terragruntOptions *options.TerragruntOptions, filename string) error { if terragruntOptions.IamRole == "" { iamConfig, err := PartialParseConfigString(configString, terragruntOptions, nil, filename, []PartialDecodeSectionType{TerragruntFlags}) if err != nil { From 3efe7f435f3062b8bca87e9435f8f7da73613965 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Thu, 30 Sep 2021 20:08:24 +0300 Subject: [PATCH 17/18] Documentation update --- docs/_docs/04_reference/built-in-functions.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/_docs/04_reference/built-in-functions.md b/docs/_docs/04_reference/built-in-functions.md index c91f8c73ab..8668e19ebd 100644 --- a/docs/_docs/04_reference/built-in-functions.md +++ b/docs/_docs/04_reference/built-in-functions.md @@ -490,6 +490,7 @@ remote_state { } } ``` +** Note: ** value returned by `get_aws_account_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## get\_aws\_caller\_identity\_arn @@ -500,6 +501,7 @@ inputs = { caller_arn = get_aws_caller_identity_arn() } ``` +** Note: ** value returned by `get_aws_caller_identity_arn()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## get\_terraform\_command @@ -543,6 +545,7 @@ terraform { } } ``` +** Note: ** value returned by `get_aws_caller_identity_user_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## run\_cmd From 3129a74fddf4993f1cb8d731db54dd02b9f23b38 Mon Sep 17 00:00:00 2001 From: Denis Obada Date: Thu, 30 Sep 2021 20:16:46 +0300 Subject: [PATCH 18/18] Updated formatting --- docs/_docs/04_reference/built-in-functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_docs/04_reference/built-in-functions.md b/docs/_docs/04_reference/built-in-functions.md index 8668e19ebd..65af73d465 100644 --- a/docs/_docs/04_reference/built-in-functions.md +++ b/docs/_docs/04_reference/built-in-functions.md @@ -490,7 +490,7 @@ remote_state { } } ``` -** Note: ** value returned by `get_aws_account_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. +**Note:** value returned by `get_aws_account_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## get\_aws\_caller\_identity\_arn @@ -501,7 +501,7 @@ inputs = { caller_arn = get_aws_caller_identity_arn() } ``` -** Note: ** value returned by `get_aws_caller_identity_arn()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. +**Note:** value returned by `get_aws_caller_identity_arn()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## get\_terraform\_command @@ -545,7 +545,7 @@ terraform { } } ``` -** Note: ** value returned by `get_aws_caller_identity_user_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. +**Note:** value returned by `get_aws_caller_identity_user_id()` can change during parsing of HCL code, for example after evaluation of `iam_role` attribute. ## run\_cmd