From c179c78aa5c7860e1f7632cdde26f4e71aac155b Mon Sep 17 00:00:00 2001 From: Anton Babenko Date: Fri, 1 Nov 2019 10:34:46 +0100 Subject: [PATCH] Added WIP files before decided to abandon this project --- README.md | 63 +++++++++- .../eu-west-1/app/terragrunt.hcl | 50 ++++++++ .../eu-west-1/core/terraform.tfvars | 7 -- .../eu-west-1/core/terragrunt.hcl | 7 ++ .../project2-terraform/app/terraform.tfvars | 20 +++- examples/project2-terraform/core/main.tf | 73 ++++++++++++ .../project2-terraform/core/terraform.tfstate | 108 ++++++++++++++++++ .../nested-stack/core/main.tf | 73 ++++++++++++ .../nested-stack/core/terraform.tfstate | 108 ++++++++++++++++++ .../project2-terraform/nested-stack/main.tf | 5 + .../nested-stack/terraform.tfstate | 16 +++ .../nested-stack/terraform.tfvars | 1 + .../app/main.tf | 12 ++ .../app/terraform.tfvars | 10 ++ .../vpc/public_subnets | 0 .../vpc/vpc_id | 0 hcl_ast.go | 7 +- main.go | 79 +++++++++---- 18 files changed, 595 insertions(+), 44 deletions(-) create mode 100644 examples/project1-terragrunt/eu-west-1/app/terragrunt.hcl delete mode 100644 examples/project1-terragrunt/eu-west-1/core/terraform.tfvars create mode 100644 examples/project1-terragrunt/eu-west-1/core/terragrunt.hcl create mode 100644 examples/project2-terraform/core/main.tf create mode 100644 examples/project2-terraform/core/terraform.tfstate create mode 100644 examples/project2-terraform/nested-stack/core/main.tf create mode 100644 examples/project2-terraform/nested-stack/core/terraform.tfstate create mode 100644 examples/project2-terraform/nested-stack/main.tf create mode 100644 examples/project2-terraform/nested-stack/terraform.tfstate create mode 100644 examples/project2-terraform/nested-stack/terraform.tfvars create mode 100644 examples/project3-terraform-remote-states/app/main.tf create mode 100644 examples/project3-terraform-remote-states/app/terraform.tfvars rename examples/{project2-terraform => project3-terraform-remote-states}/vpc/public_subnets (100%) rename examples/{project2-terraform => project3-terraform-remote-states}/vpc/vpc_id (100%) diff --git a/README.md b/README.md index 32844d8..c4663cb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ [Terraform](https://www.terraform.io/) is awesome! -As of today, Terraform 0.11 and 0.12 support only static (known, fixed, already computed) values in `tfvars` files. There is no way to use Terraform interpolation functions, or data-sources inside `tfvars` files in Terraform to update values. +Terraform 0.11 and 0.12 supports only static (known, fixed, already computed) values in `tfvars` files. There is no way to use Terraform functions, or data-sources inside `tfvars` files in Terraform to update values. + +Using Terraform modules (eg, terraform-aws-modules) developers are not restricted on specific ways how module arguments can be provided (eg, from modules, remote states, other data-sources, command line arguments, tfvars files, etc). Using tfvars files is often a very good idea, because it separates configurations and values! And this is exactly where `tfvars-annotations` tool helps. While working on [modules.tf](https://github.com/antonbabenko/modules.tf-lambda) (a tool which converts visual diagrams created with [Cloudcraft.co](https://cloudcraft.co/) into Terraform configurations), I had a need to generate code which would chain invocations of [Terraform AWS modules](https://github.com/terraform-aws-modules) and pass arguments between them without requiring any extra Terraform code as a glue. [Terragrunt](https://github.com/gruntwork-io/terragrunt) is a great fit for this, it allows to reduce amount of Terraform configurations by reusing Terraform modules and providing arguments as values in `tfvars` files. @@ -23,13 +25,56 @@ Some languages I know have concepts like annotations and decorators, so at first - [x] terragrunt_output: - `@tfvars:terragrunt_output.vpc.vpc_id` - `@tfvars:terragrunt_output.security-group.this_security_group_id` - - [ ] terraform_output + - [x] terraform_output + - `@tfvars:terraform_output.vpc.vpc_id` + - `@tfvars:terraform_output.security-group.this_security_group_id` - [ ] data-sources generic -1. Type wrapping: - - `to_list`: Wrap original value with `[]` to make it it as a list + - `@tfvars:terragrunt_data.aws_region.zone_id` ? + - `@tfvars:terraform_data.aws_region.zone_id` ? + +## General form of the annotations: + +`@tfvars:TYPE.DIRECTORY.ATTR[.RETURN_TYPE]` + +`TYPE` - Type of the fetcher: +1. terragrunt_output - Get value from `terragrunt output ` +1. terraform_output - Get value from `terraform output ` +1. terraform_data and terragrunt_data - Get attribute from data-source (eg, `aws_caller_identity.account_id` to ret) + +`DIRECTORY` - Working directory where `TYPE` commands should be run. It can be specified in short form if it is a name of directory on the same level as parent directory. +Long form should be used to specify directories on the same level as well as from parent (no max level). + +Examples: +1. `[./core]` and `[core]` are identical and refer to `core` subfolder, `[../../../core]` - several folders above +1. `[../core]` and `core` have identical meanings and refer to `core` in the parent directory +1. `[..]` - parent directory +1. `[.]` - current directory + +`ATTR` - Name of output to return or name of attribute if `TYPE` is data-source (terraform_data and terragrunt_data). + +`RETURN_TYPE` - (Optional) Specify desired type to return. `to_list` can be used to wrap original value with `[]` to make it as a list. + +## Examples of various annotations + +1. `@tfvars:terragrunt_output.vpc.vpc_id` +1. `@tfvars:terragrunt_output.[../../vpc].vpc_id` +1. `@tfvars:terraform_output.[./vpc].vpc_id.to_list` +1. `@tfvars:terraform_output.[../us-east-1/acm].this_acm_arn` +1. `@tfvars:terragrunt_data.aws_region.zone_id` +1. `@tfvars:terragrunt_data.aws_caller_identity.account_id.to_list` ## How to use +``` +Usage: tfvars-annotations [DIR] + -debug + enable debug logging + -version + print version information and exit + +[DIR] (optional) - If not specified current directory will be used. +``` + Run `tfvars-annotations` before `terraform plan, apply, refresh`. It will process tfvars file in the current directory and set updated values. @@ -38,7 +83,9 @@ E.g.: $ tfvars-annotations examples/project1-terragrunt/eu-west-1/app $ terraform plan - + +## Annotations + ## How to disable processing entirely Put `@tfvars:disable_annotations` anywhere in the `terraform.tfvars` to not process the file. @@ -50,8 +97,9 @@ See `examples` for some basics. ## To-do 1. Get values from other sources: - - data sources generic + - data sources generic (with missing values to use the default value) - aws_account_id or aws_region data sources + - aws_account_id by alias (reference by alias is friendlier) 2. terragrunt_outputs from stacks: - in any folder - in current region @@ -60,6 +108,8 @@ See `examples` for some basics. 5. rewrite in go (invoke like this => update_dynamic_values_in_tfvars ${get_parent_tfvars_dir()}/${path_relative_to_include()}) 6. make it much faster, less verbose 7. add dry-run flag +9. Add -detailed-exitcode to know if files were changed +10. Support terraform.tfvars and terragrunt.hcl or custom list of files 8. Proposed syntax: - `@tfvars:terragrunt_output.security-group_5.this_security_group_id.to_list` @@ -71,6 +121,7 @@ See `examples` for some basics. - `@tfvars:terragrunt_data.aws_region.zone_id` - `@tfvars:terragrunt_data.aws_region[{current=true}].zone_id` +// 9. Describe and compare tfvars-annotations vs using remote_state ## Bugs diff --git a/examples/project1-terragrunt/eu-west-1/app/terragrunt.hcl b/examples/project1-terragrunt/eu-west-1/app/terragrunt.hcl new file mode 100644 index 0000000..cc3aa43 --- /dev/null +++ b/examples/project1-terragrunt/eu-west-1/app/terragrunt.hcl @@ -0,0 +1,50 @@ +terragrunt = { + terraform = { + source = "." + } +} + +dependency "core" { + path = "../core" +} + +################ +# Static values +################ + +title = "This value is not going to be changed by tfvars-annotations" + +################# +# Dynamic values +################# + +name = "" # @tfvars:terragrunt_output.core.name + +score = "" # @tfvars:terragrunt_output.core.score + +name_as_list = [""] # @tfvars:terragrunt_output.core.name.to_list + +love_sailing = "" # @tfvars:terragrunt_output.core.love_sailing + +understand_how_to_use_twitter = "" # @tfvars:terragrunt_output.core.understand_how_to_use_twitter + +languages = "" # @tfvars:terragrunt_output.core.languages + +############### +# Compositions +############### + +custom_map = { + Score = "" # @tfvars:terragrunt_output.core.score + Name = "" # @tfvars:terragrunt_output.core.name + MixedValue = "" # @ tfvars:terragrunt_output.core.mixed_value <-- same reason as below. Maps are tricky. +} + +###### +# These don't work yet because there are `maps` inside of them. +###### +list_of_properties = "" # @ tfvars:terragrunt_output.core.list_of_properties + +map_of_properties = "" # @ tfvars:terragrunt_output.core.map_of_properties + +mixed_value = "" # @ tfvars:terragrunt_output.core.mixed_value diff --git a/examples/project1-terragrunt/eu-west-1/core/terraform.tfvars b/examples/project1-terragrunt/eu-west-1/core/terraform.tfvars deleted file mode 100644 index 630c399..0000000 --- a/examples/project1-terragrunt/eu-west-1/core/terraform.tfvars +++ /dev/null @@ -1,7 +0,0 @@ -terragrunt = { - terraform = { - source = "." - } -} - -name = "test" diff --git a/examples/project1-terragrunt/eu-west-1/core/terragrunt.hcl b/examples/project1-terragrunt/eu-west-1/core/terragrunt.hcl new file mode 100644 index 0000000..0662924 --- /dev/null +++ b/examples/project1-terragrunt/eu-west-1/core/terragrunt.hcl @@ -0,0 +1,7 @@ +terraform { + source = "." +} + +inputs = { + name = "test" +} \ No newline at end of file diff --git a/examples/project2-terraform/app/terraform.tfvars b/examples/project2-terraform/app/terraform.tfvars index 1f65316..e759372 100644 --- a/examples/project2-terraform/app/terraform.tfvars +++ b/examples/project2-terraform/app/terraform.tfvars @@ -1,11 +1,19 @@ -# @ modulestf:disable_values_updates +# @ tfvars:disable_annotations -vpc_id = "vpc-443a8116aae25c7e9" # @modulestf:terraform_output.vpc.vpc_id +name = "Anton Babenko" # @tfvars:terraform_output.core.name -public_subnets = ["subnet-297e8b509d8aeebfe","subnet-3f831847e5802071c","subnet-6c6f959a9063d32b2"] # @modulestf:terraform_output.vpc.public_subnets +score = "37" # @tfvars:terraform_output.core.score -something = "" # @modulestf:terraform_output.something.id +name_as_list = [""] # @tfvars:terraform_output.core.name.to_list -vpc4_id = "vpc-443a8116aae25c7e9" # @modulestf:terraform_output.vpc.vpc_id +love_sailing = "true" # @tfvars:terraform_output.core.love_sailing -the end! \ No newline at end of file +understand_how_to_use_twitter = "false" # @tfvars:terraform_output.core.understand_how_to_use_twitter + +languages = [ + "ukrainian", + "russian", + "english", + "norwegian", + "spanish", +] # @tfvars:terraform_output.core.languages diff --git a/examples/project2-terraform/core/main.tf b/examples/project2-terraform/core/main.tf new file mode 100644 index 0000000..e833028 --- /dev/null +++ b/examples/project2-terraform/core/main.tf @@ -0,0 +1,73 @@ +output "name" { + value = "Anton Babenko" +} + +output "score" { + value = "37" +} + +output "love_sailing" { + value = "true" +} + +output "understand_how_to_use_twitter" { + value = "false" +} + +output "languages" { + value = ["ukrainian", "russian", "english", "norwegian", "spanish"] +} + +output "map_of_properties" { + value = { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + } +} + +output "list_of_properties" { + value = [ + { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + UnderstandHowToUseTwitter = false + }, + { + Name = "Kapitoshka" + Age = 123 + LoveSailing = false + }, + ] +} + +output "mixed_value" { + value = [ + "This is just a string", + [ + { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + UnderstandHowToUseTwitter = false + }, + ], + { + Github = "antonbabenko" + Twitter = "antonbabenko" + }, + ] +} + +// Failing values ("=" inside values): +output "failing_values_list" { + value = ["ukrainian = 100", "english", "unknown"] +} + +output "failing_values_map" { + value = { + Name = "Anton Babenko" + Age = 34 + } +} diff --git a/examples/project2-terraform/core/terraform.tfstate b/examples/project2-terraform/core/terraform.tfstate new file mode 100644 index 0000000..248ba62 --- /dev/null +++ b/examples/project2-terraform/core/terraform.tfstate @@ -0,0 +1,108 @@ +{ + "version": 3, + "terraform_version": "0.11.13", + "serial": 1, + "lineage": "71a91e74-7bee-1d3e-0202-e36cd675d7ac", + "modules": [ + { + "path": [ + "root" + ], + "outputs": { + "failing_values_list": { + "sensitive": false, + "type": "list", + "value": [ + "ukrainian = 100", + "english", + "unknown" + ] + }, + "failing_values_map": { + "sensitive": false, + "type": "map", + "value": { + "Age": 34, + "Name": "Anton Babenko" + } + }, + "languages": { + "sensitive": false, + "type": "list", + "value": [ + "ukrainian", + "russian", + "english", + "norwegian", + "spanish" + ] + }, + "list_of_properties": { + "sensitive": false, + "type": "list", + "value": [ + { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko", + "UnderstandHowToUseTwitter": false + }, + { + "Age": 123, + "LoveSailing": false, + "Name": "Kapitoshka" + } + ] + }, + "love_sailing": { + "sensitive": false, + "type": "string", + "value": "true" + }, + "map_of_properties": { + "sensitive": false, + "type": "map", + "value": { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko" + } + }, + "mixed_value": { + "sensitive": false, + "type": "list", + "value": [ + "This is just a string", + { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko", + "UnderstandHowToUseTwitter": false + }, + { + "Github": "antonbabenko", + "Twitter": "antonbabenko" + } + ] + }, + "name": { + "sensitive": false, + "type": "string", + "value": "Anton Babenko" + }, + "score": { + "sensitive": false, + "type": "string", + "value": "37" + }, + "understand_how_to_use_twitter": { + "sensitive": false, + "type": "string", + "value": "false" + } + }, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/examples/project2-terraform/nested-stack/core/main.tf b/examples/project2-terraform/nested-stack/core/main.tf new file mode 100644 index 0000000..e833028 --- /dev/null +++ b/examples/project2-terraform/nested-stack/core/main.tf @@ -0,0 +1,73 @@ +output "name" { + value = "Anton Babenko" +} + +output "score" { + value = "37" +} + +output "love_sailing" { + value = "true" +} + +output "understand_how_to_use_twitter" { + value = "false" +} + +output "languages" { + value = ["ukrainian", "russian", "english", "norwegian", "spanish"] +} + +output "map_of_properties" { + value = { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + } +} + +output "list_of_properties" { + value = [ + { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + UnderstandHowToUseTwitter = false + }, + { + Name = "Kapitoshka" + Age = 123 + LoveSailing = false + }, + ] +} + +output "mixed_value" { + value = [ + "This is just a string", + [ + { + Name = "Anton Babenko" + Age = 34 + LoveSailing = true + UnderstandHowToUseTwitter = false + }, + ], + { + Github = "antonbabenko" + Twitter = "antonbabenko" + }, + ] +} + +// Failing values ("=" inside values): +output "failing_values_list" { + value = ["ukrainian = 100", "english", "unknown"] +} + +output "failing_values_map" { + value = { + Name = "Anton Babenko" + Age = 34 + } +} diff --git a/examples/project2-terraform/nested-stack/core/terraform.tfstate b/examples/project2-terraform/nested-stack/core/terraform.tfstate new file mode 100644 index 0000000..248ba62 --- /dev/null +++ b/examples/project2-terraform/nested-stack/core/terraform.tfstate @@ -0,0 +1,108 @@ +{ + "version": 3, + "terraform_version": "0.11.13", + "serial": 1, + "lineage": "71a91e74-7bee-1d3e-0202-e36cd675d7ac", + "modules": [ + { + "path": [ + "root" + ], + "outputs": { + "failing_values_list": { + "sensitive": false, + "type": "list", + "value": [ + "ukrainian = 100", + "english", + "unknown" + ] + }, + "failing_values_map": { + "sensitive": false, + "type": "map", + "value": { + "Age": 34, + "Name": "Anton Babenko" + } + }, + "languages": { + "sensitive": false, + "type": "list", + "value": [ + "ukrainian", + "russian", + "english", + "norwegian", + "spanish" + ] + }, + "list_of_properties": { + "sensitive": false, + "type": "list", + "value": [ + { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko", + "UnderstandHowToUseTwitter": false + }, + { + "Age": 123, + "LoveSailing": false, + "Name": "Kapitoshka" + } + ] + }, + "love_sailing": { + "sensitive": false, + "type": "string", + "value": "true" + }, + "map_of_properties": { + "sensitive": false, + "type": "map", + "value": { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko" + } + }, + "mixed_value": { + "sensitive": false, + "type": "list", + "value": [ + "This is just a string", + { + "Age": 34, + "LoveSailing": true, + "Name": "Anton Babenko", + "UnderstandHowToUseTwitter": false + }, + { + "Github": "antonbabenko", + "Twitter": "antonbabenko" + } + ] + }, + "name": { + "sensitive": false, + "type": "string", + "value": "Anton Babenko" + }, + "score": { + "sensitive": false, + "type": "string", + "value": "37" + }, + "understand_how_to_use_twitter": { + "sensitive": false, + "type": "string", + "value": "false" + } + }, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/examples/project2-terraform/nested-stack/main.tf b/examples/project2-terraform/nested-stack/main.tf new file mode 100644 index 0000000..de0a9dd --- /dev/null +++ b/examples/project2-terraform/nested-stack/main.tf @@ -0,0 +1,5 @@ +variable "name" {} + +output "welcome" { + value = "Hello, ${var.name}!" +} \ No newline at end of file diff --git a/examples/project2-terraform/nested-stack/terraform.tfstate b/examples/project2-terraform/nested-stack/terraform.tfstate new file mode 100644 index 0000000..3c657ed --- /dev/null +++ b/examples/project2-terraform/nested-stack/terraform.tfstate @@ -0,0 +1,16 @@ +{ + "version": 3, + "terraform_version": "0.11.14", + "serial": 1, + "lineage": "67df8ff9-0551-3d51-4c1f-c5a06ce23484", + "modules": [ + { + "path": [ + "root" + ], + "outputs": {}, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/examples/project2-terraform/nested-stack/terraform.tfvars b/examples/project2-terraform/nested-stack/terraform.tfvars new file mode 100644 index 0000000..bc38ff4 --- /dev/null +++ b/examples/project2-terraform/nested-stack/terraform.tfvars @@ -0,0 +1 @@ +name = "Anton Babenko" # @tfvars:terraform_output.[./core].name diff --git a/examples/project3-terraform-remote-states/app/main.tf b/examples/project3-terraform-remote-states/app/main.tf new file mode 100644 index 0000000..885f0da --- /dev/null +++ b/examples/project3-terraform-remote-states/app/main.tf @@ -0,0 +1,12 @@ +data "terraform_remote_state" "vpc" { + backend = "s3" + + config { + bucket = "my-tfstates" + key = "vpc/terraform.tfstate" + } +} + +output "vpc_id" { + value = "${data.terraform_remote_state.vpc.vpc_id}" +} \ No newline at end of file diff --git a/examples/project3-terraform-remote-states/app/terraform.tfvars b/examples/project3-terraform-remote-states/app/terraform.tfvars new file mode 100644 index 0000000..8f0bf61 --- /dev/null +++ b/examples/project3-terraform-remote-states/app/terraform.tfvars @@ -0,0 +1,10 @@ +vpc_id1 = "vpc-443a8116aae25c7e9" # @tfvars:terraform_data.terraform_remote_state.vpc.vpc_id + +vpc_id2 = "vpc-443a8116aae25c7e9" # @tfvars:terraform_output.vpc.vpc_id + +# @tfvars:config:terraform_output.vpc = { +# method = "output" # <- this will run "terraform output" +# } +# @tfvars:config:terraform_output = { +# method = "terraform_remote_state" # <- this will produce terraform code with data-source and run "terraform refresh" and "terraform output" +# } diff --git a/examples/project2-terraform/vpc/public_subnets b/examples/project3-terraform-remote-states/vpc/public_subnets similarity index 100% rename from examples/project2-terraform/vpc/public_subnets rename to examples/project3-terraform-remote-states/vpc/public_subnets diff --git a/examples/project2-terraform/vpc/vpc_id b/examples/project3-terraform-remote-states/vpc/vpc_id similarity index 100% rename from examples/project2-terraform/vpc/vpc_id rename to examples/project3-terraform-remote-states/vpc/vpc_id diff --git a/hcl_ast.go b/hcl_ast.go index 087114a..e30b90c 100644 --- a/hcl_ast.go +++ b/hcl_ast.go @@ -28,7 +28,7 @@ var ( tfvarsDisableAnnotations = `@tfvars:disable_annotations` // Regexp - tfvarsTerragruntOutputRegexp = regexp.MustCompile(`@tfvars:terragrunt_output\.[^ \n]+`) + tfvarsTerragruntOutputRegexp = regexp.MustCompile(`@tfvars:(terragrunt_output|terraform_output)\.[^ \n]+`) _ = spew.Config _ = fmt.Sprint() @@ -46,14 +46,13 @@ func parseContent(hclString *string) (*ast.File, error) { func scanComments(astf *ast.File) (bool, []string) { comments := astf.Comments - isDisabled := false keysFound := []string{} for _, commentList := range comments { for _, comment := range commentList.List { if strings.Contains(comment.Text, tfvarsDisableAnnotations) { - isDisabled = true + return true, []string{} } allKeys := tfvarsTerragruntOutputRegexp.FindAllString(comment.Text, -1) @@ -68,7 +67,7 @@ func scanComments(astf *ast.File) (bool, []string) { log.Debugf("Found keys: %s", keysFound) - return isDisabled, keysFound + return false, keysFound } func updateValuesInTfvarsFile(astf *ast.File, allKeyValues map[string]interface{}) (ast.File, []string) { diff --git a/main.go b/main.go index bf91635..f8313cd 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,8 @@ var ( debug = flag.Bool("debug", false, "enable debug logging") // Main filename to work with - tfvarsFile = "terraform.tfvars" + //tfvarsFile = "terraform.tfvars" + tfvarsFile = "terragrunt.hcl" // Dir where terragrunt cache lives terragruntCacheDir = ".terragrunt-cache" @@ -86,7 +87,11 @@ func main() { log.Infoln("Working dir: ", terraformWorkingDir) // Full relative path to destination tfvars file (inside .terragrunt-cache/.../.../.terraform) - var terraformWorkingDirTfvarsFullPath = filepath.Join(terraformWorkingDir, tfvarsFile) + var terraformWorkingDirTfvarsFullPath = "" + + if terraformWorkingDir != "" { + terraformWorkingDirTfvarsFullPath = filepath.Join(terraformWorkingDir, tfvarsFile) + } // Map of all keys and values to replace in tfvars file allKeyValues := make(map[string]interface{}) @@ -117,8 +122,14 @@ func main() { for _, key := range keysToReplace { log.Infof("Key: %s", key) - split := strings.Split(key, ".") + // Remove "@tfvars:" from the beginning of the key + keyWithPrefix := strings.SplitAfter(key, ":") + + keyWithoutPrefix := strings.Join(keyWithPrefix[1:], ":") + split := strings.Split(keyWithoutPrefix, ".") + + sourceType := "" dirName := "" outputName := "" convertToType := "" @@ -127,6 +138,10 @@ func main() { continue } + if len(split) > 0 { + sourceType = split[0] + } + if len(split) > 1 { dirName = split[1] } @@ -142,7 +157,23 @@ func main() { workDir := filepath.Join(tfvarsDir, "../", dirName) //fmt.Println(workDir) - resultValue, resultType, errResult := getResultFromTerragruntOutput(workDir, outputName) + spew.Dump(sourceType) + + var resultValue interface{} + var resultType string + var errResult error + + switch sourceType { + case "terragrunt_output": + resultValue, resultType, errResult = getResultFromTerragruntOutput(workDir, outputName) + case "terraform_output": + resultValue, resultType, errResult = getResultFromTerraformOutput(workDir, outputName) + //case "aws_data": + + } + + spew.Dump(resultValue) + //resultValue, resultType, errResult := getResultFromTerragruntOutput(workDir, outputName) if errResult != nil { log.Warnf("Can't update value of %s in %s because key \"%s\"", key, tfvarsFullpath, outputName) @@ -153,11 +184,6 @@ func main() { _ = resultType _ = convertToType - // @todo: add support for to_list - //if convertToType == "to_list" { - // resultValue = fmt.Sprintf("[%s]", resultValue) - //} - allKeyValues[key] = resultValue log.Infof("Value: %s", spew.Sdump(resultValue)) @@ -193,14 +219,16 @@ func main() { log.Infoln(string(hclFormatted)) _ = hclFormatted - log.Infoln() - log.Infof("Copying updated %s into %s", tfvarsFullpath, terraformWorkingDirTfvarsFullPath) - log.Infoln() + if terraformWorkingDirTfvarsFullPath != "" { + log.Infoln() + log.Infof("Copying updated %s into %s", tfvarsFullpath, terraformWorkingDirTfvarsFullPath) + log.Infoln() - _, err = util.CopyFile(tfvarsFullpath, terraformWorkingDirTfvarsFullPath) + _, err = util.CopyFile(tfvarsFullpath, terraformWorkingDirTfvarsFullPath) - if err != nil { - log.Fatalf("%s: Can't copy file to %s", err, terraformWorkingDirTfvarsFullPath) + if err != nil { + log.Fatalf("%s: Can't copy file to %s", err, terraformWorkingDirTfvarsFullPath) + } } log.Infoln("Done!") @@ -235,27 +263,36 @@ func readTfvarsFile(tfvarsFullpath string) (string, error) { return string(bytes), nil } +func getResultFromTerraformOutput(dirName string, outputName string) (interface{}, string, error) { + return getResultFromTerraOutput("terraform", dirName, outputName) +} + func getResultFromTerragruntOutput(dirName string, outputName string) (interface{}, string, error) { + return getResultFromTerraOutput("terragrunt", dirName, outputName) +} + +func getResultFromTerraOutput(binary string, dirName string, outputName string) (interface{}, string, error) { - lsCmd := exec.Command("terragrunt", "output", "-json", outputName) + lsCmd := exec.Command(binary, "output", "-json", outputName) //lsCmd := exec.Command("cat", outputName) lsCmd.Dir = dirName lsOut, err := lsCmd.Output() + log.Debugf("Running %s in %s", lsCmd.Path, lsCmd.Dir) if err != nil { - log.Debugln(spew.Sdump(lsCmd)) + //log.Debugln(spew.Sdump(lsCmd)) - return "", "", errors.Wrapf(err, "running terragrunt output -json %s", outputName) + return "", "", errors.Wrapf(err, "running %s output -json %s", binary, outputName) } //fmt.Println("terragrunt value = ", string(lsOut)) // Unmarshal output into JSON - var TerragruntOutput map[string]interface{} + var TerraOutput map[string]interface{} - if err := json.Unmarshal(lsOut, &TerragruntOutput); err != nil { + if err := json.Unmarshal(lsOut, &TerraOutput); err != nil { panic(err) } - return TerragruntOutput["value"], TerragruntOutput["type"].(string), nil + return TerraOutput["value"], TerraOutput["type"].(string), nil }