Skip to content

Commit

Permalink
Merge pull request #128 from realloc/terraform_args
Browse files Browse the repository at this point in the history
Adding `extra_arguments` config option
  • Loading branch information
brikis98 authored Feb 17, 2017
2 parents 47489ff + 8be875c commit 43ae86f
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 4 deletions.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,57 @@ terragrunt = {
}
```

### Passing extra command line arguments to Terraform

Sometimes you may need to pass extra arguments to Terraform on each run. For example if you have a separate file with
secret variables you may use extra_arguments option in terraform section of Terragrunt configuration to do it
automatically.

Each set of arguments will be appended only if current Terraform command is in `commands` list. If more than one set is
applicable, they will be added in the order of of appearance in config.

Sample config:

``` hcl
terragrunt= {
terraform = {
{
extra_arguments "secrets" {
arguments = [
"-var-file=terraform.tfvars",
"-var-file=terraform-secret.tfvars"
]
commands = [
"apply",
"plan",
"import",
"push",
"refresh"
]
}
extra_arguments "json_output" {
arguments = [
"-json"
]
commands = [
"output"
]
}
extra_arguments "fmt_diff" {
arguments = [
"-diff=true"
]
commands = [
"fmt"
]
}
}
}
```

### The spin-up and tear-down commands

Let's say you have a single environment (e.g. `stage` or `prod`) that has a number of Terraform modules within it:
Expand Down
19 changes: 18 additions & 1 deletion cli/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,23 @@ func parseTerragruntOptionsFromArgs(args []string) (*options.TerragruntOptions,
}, nil
}

func filterTerraformExtraArgs(terragruntOptions *options.TerragruntOptions, terragruntConfig *config.TerragruntConfig) []string {
out := []string{}
cmd := firstArg(terragruntOptions.TerraformCliArgs)

for _, arg := range terragruntConfig.Terraform.ExtraArgs {
if arg.Commands != nil && arg.Arguments != nil {
for _, arg_cmd := range arg.Commands {
if cmd == arg_cmd {
out = append(out, arg.Arguments...)
}
}
}
}

return out
}

func parseEnvironmentVariables(environment []string) map[string]string {
environmentMap := make(map[string]string)

Expand Down Expand Up @@ -167,4 +184,4 @@ func secondArg(args []string) string {
type ArgMissingValue string
func (err ArgMissingValue) Error() string {
return fmt.Sprintf("You must specify a value for the --%s option", string(err))
}
}
6 changes: 5 additions & 1 deletion cli/cli_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func runTerragrunt(terragruntOptions *options.TerragruntOptions) error {
return err
}

if conf.Terraform != nil && conf.Terraform.ExtraArgs != nil && len(conf.Terraform.ExtraArgs) > 0 {
terragruntOptions.TerraformCliArgs = append(terragruntOptions.TerraformCliArgs, filterTerraformExtraArgs(terragruntOptions, conf)...)
}

if sourceUrl, hasSourceUrl := getTerraformSourceUrl(terragruntOptions, conf); hasSourceUrl {
if err := downloadTerraformSource(sourceUrl, terragruntOptions); err != nil {
return err
Expand Down Expand Up @@ -330,4 +334,4 @@ var DontManuallyConfigureRemoteState = fmt.Errorf("Instead of manually using the
type UnrecognizedCommand string
func (commandName UnrecognizedCommand) Error() string {
return fmt.Sprintf("Unrecognized command: %s", string(commandName))
}
}
16 changes: 14 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,24 @@ func (deps *ModuleDependencies) String() string {

// TerraformConfig specifies where to find the Terraform configuration files
type TerraformConfig struct {
Source string `hcl:"source"`
ExtraArgs []TerraformExtraArguments `hcl:"extra_arguments"`
Source string `hcl:"source"`
}

func (conf *TerraformConfig) String() string {
return fmt.Sprintf("TerraformConfig{Source = %v}", conf.Source)
}

// TerraformExtraArguments sets a list of arguments to pass to Terraform if command fits any in the `Commands` list
type TerraformExtraArguments struct {
Name string `hcl:",key"`
Arguments []string `hcl:"arguments,omitempty"`
Commands []string `hcl:"commands,omitempty"`
}
func (conf *TerraformExtraArguments) String() string {
return fmt.Sprintf("TerraformArguments{Name = %s, Arguments = %v, Commands = %v}", conf.Name, conf.Arguments, conf.Commands)
}

// Return the default path to use for the Terragrunt configuration file. The reason this is a method rather than a
// constant is that older versions of Terragrunt stored configuration in a different file. This method returns the
// path to the old configuration format if such a file exists and the new format otherwise.
Expand Down Expand Up @@ -326,4 +338,4 @@ func (err TooManyLevelsOfInheritance) Error() string {
type CouldNotResolveTerragruntConfigInFile string
func (err CouldNotResolveTerragruntConfigInFile) Error() string {
return fmt.Sprintf("Could not find Terragrunt configuration settings in %s", string(err))
}
}
100 changes: 100 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,106 @@ terragrunt = {
}
}

func TestParseTerragruntConfigTerraformWithExtraArguments(t *testing.T) {
t.Parallel()

config :=
`
terragrunt = {
terraform {
extra_arguments "secrets" {
arguments = [
"-var-file=terraform.tfvars",
"-var-file=terraform-secret.tfvars"
]
commands = [
"apply",
"plan",
"import",
"push",
"refresh"
]
}
}
}
`

terragruntConfig, err := parseConfigString(config, &mockOptions, nil, DefaultTerragruntConfigPath)
if err != nil {
t.Fatal(err)
}

assert.Nil(t, terragruntConfig.Lock)
assert.Nil(t, terragruntConfig.RemoteState)
assert.Nil(t, terragruntConfig.Dependencies)

if assert.NotNil(t, terragruntConfig.Terraform) {
assert.Equal(t, "secrets", terragruntConfig.Terraform.ExtraArgs[0].Name)
assert.Equal(t,
[]string{
"-var-file=terraform.tfvars",
"-var-file=terraform-secret.tfvars",
},
terragruntConfig.Terraform.ExtraArgs[0].Arguments)
assert.Equal(t,
[]string{
"apply",
"plan",
"import",
"push",
"refresh",
},
terragruntConfig.Terraform.ExtraArgs[0].Commands)
}
}

func TestParseTerragruntConfigTerraformWithMultipleExtraArguments(t *testing.T) {
t.Parallel()

config :=
`
terragrunt = {
terraform {
extra_arguments "json_output" {
arguments = [
"-json"
]
commands = [
"output"
]
}
extra_arguments "fmt_diff" {
arguments = [
"-diff=true"
]
commands = [
"fmt"
]
}
}
}
`

terragruntConfig, err := parseConfigString(config, &mockOptions, nil, DefaultTerragruntConfigPath)
if err != nil {
t.Fatal(err)
}

assert.Nil(t, terragruntConfig.Lock)
assert.Nil(t, terragruntConfig.RemoteState)
assert.Nil(t, terragruntConfig.Dependencies)

if assert.NotNil(t, terragruntConfig.Terraform) {
assert.Equal(t, "json_output", terragruntConfig.Terraform.ExtraArgs[0].Name)
assert.Equal(t, []string{"-json"}, terragruntConfig.Terraform.ExtraArgs[0].Arguments)
assert.Equal(t, []string{"output"}, terragruntConfig.Terraform.ExtraArgs[0].Commands)
assert.Equal(t, "fmt_diff", terragruntConfig.Terraform.ExtraArgs[1].Name)
assert.Equal(t, []string{"-diff=true"}, terragruntConfig.Terraform.ExtraArgs[1].Arguments)
assert.Equal(t, []string{"fmt"}, terragruntConfig.Terraform.ExtraArgs[1].Commands)
}
}

func TestFindConfigFilesInPathNone(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions test/fixture-extra-args/extra.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extra_var = "Hello, World!"
7 changes: 7 additions & 0 deletions test/fixture-extra-args/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
variable "extra_var" {
description = "Should be loaded from extra.tfvars"
}

output "test" {
value = "${var.extra_var}"
}
17 changes: 17 additions & 0 deletions test/fixture-extra-args/terraform.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
terragrunt= {
terraform = {
extra_arguments "test" {
arguments = [
"-var-file=terraform.tfvars",
"-var-file=extra.tfvars"
]
commands = [
"apply",
"plan",
"import",
"push",
"refresh"
]
}
}
}
6 changes: 6 additions & 0 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
TEST_FIXTURE_INCLUDE_PATH = "fixture-include/"
TEST_FIXTURE_INCLUDE_CHILD_REL_PATH = "qa/my-app"
TEST_FIXTURE_STACK = "fixture-stack/"
TEST_FIXTURE_EXTRA_ARGS_PATH = "fixture-extra-args/"
TEST_FIXTURE_LOCAL_DOWNLOAD_PATH = "fixture-download/local"
TEST_FIXTURE_REMOTE_DOWNLOAD_PATH = "fixture-download/remote"
TEST_FIXTURE_OVERRIDE_DOWNLOAD_PATH = "fixture-download/override"
Expand Down Expand Up @@ -281,6 +282,11 @@ func TestRemoteDownloadOverride(t *testing.T) {
runTerragrunt(t, fmt.Sprintf("terragrunt apply --terragrunt-non-interactive --terragrunt-working-dir %s --terragrunt-source %s", TEST_FIXTURE_OVERRIDE_DOWNLOAD_PATH, "../hello-world"))
}

func TestExtraArguments(t *testing.T) {
t.Parallel()
runTerragrunt(t, fmt.Sprintf("terragrunt apply --terragrunt-non-interactive --terragrunt-working-dir %s", TEST_FIXTURE_EXTRA_ARGS_PATH))
}

func cleanupTerraformFolder(t *testing.T, templatesPath string) {
removeFile(t, util.JoinPath(templatesPath, TERRAFORM_STATE))
removeFile(t, util.JoinPath(templatesPath, TERRAFORM_STATE_BACKUP))
Expand Down

0 comments on commit 43ae86f

Please sign in to comment.