Skip to content

Commit 0720b5b

Browse files
Fixes issues with AWSAssumeRole in Blocks for Terraform being passed in (#1720)
* Fixes issues with AWSAssumeRole in Blocks for Terraform being passed in
1 parent b4bc8f3 commit 0720b5b

File tree

5 files changed

+113
-39
lines changed

5 files changed

+113
-39
lines changed

libs/digger_config/digger_config.go

+10-2
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,22 @@ func HandleYamlProjectGeneration(config *DiggerConfigYaml, terraformDir string,
269269
workflow = b.Workflow
270270
}
271271

272-
err := hydrateDiggerConfigYamlWithTerragrunt(config, TerragruntParsingConfig{
272+
tgParsingConfig := TerragruntParsingConfig{
273273
CreateProjectName: true,
274274
DefaultWorkflow: workflow,
275275
WorkflowFile: b.WorkflowFile,
276276
FilterPath: path.Join(terraformDir, *b.RootDir),
277-
}, terraformDir)
277+
};
278+
279+
// allow blocks to pass in roles that can be assummed by aws
280+
tgParsingConfig.AwsRoleToAssume = b.AwsRoleToAssume
281+
282+
283+
err := hydrateDiggerConfigYamlWithTerragrunt(config, tgParsingConfig, terraformDir)
278284
if err != nil {
279285
return err
280286
}
287+
281288
}
282289
} else {
283290
includePatterns = []string{b.Include}
@@ -500,6 +507,7 @@ func hydrateDiggerConfigYamlWithTerragrunt(configYaml *DiggerConfigYaml, parsing
500507
WorkflowFile: &workflowFile,
501508
IncludePatterns: atlantisProject.Autoplan.WhenModified,
502509
Generated: true,
510+
AwsRoleToAssume: parsingConfig.AwsRoleToAssume,
503511
})
504512
}
505513
return nil

libs/digger_config/yaml.go

+1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ type TerragruntParsingConfig struct {
142142
UseProjectMarkers bool `yaml:"useProjectMarkers"`
143143
ExecutionOrderGroups *bool `yaml:"executionOrderGroups"`
144144
WorkflowFile string `yaml:"workflow_file"`
145+
AwsRoleToAssume *AssumeRoleForProjectConfig `yaml:"aws_role_to_assume,omitempty"`
145146
}
146147

147148
func (p *ProjectYaml) UnmarshalYAML(unmarshal func(interface{}) error) error {

libs/execution/execution.go

+14
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os"
1313
"path"
1414
"regexp"
15+
"runtime"
1516
"strconv"
1617
"strings"
1718

@@ -217,6 +218,7 @@ func (d DiggerExecutor) Plan() (*terraform_utils.TerraformSummary, bool, bool, s
217218
}
218219
}
219220
for _, step := range planSteps {
221+
log.Printf(" Running step: %v\n", step.Action)
220222
if step.Action == "init" {
221223
_, stderr, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars)
222224
if err != nil {
@@ -531,3 +533,15 @@ func cleanupTerraformPlan(nonEmptyPlan bool, planError error, stdout string, std
531533
func (d DiggerExecutor) projectId() string {
532534
return d.ProjectNamespace + "#" + d.ProjectName
533535
}
536+
537+
// this will log an exit code and error based on the executor of the executor drivers are by filename
538+
func logCommandFail(exitCode int, err error) {
539+
540+
_, filename, _, ok := runtime.Caller(1);
541+
if ok {
542+
executor := strings.TrimSuffix(path.Base(filename), path.Ext(filename))
543+
log.Printf("Command failed in %v with exit code %v and error %v", executor, exitCode, err)
544+
} else {
545+
log.Printf("Command failed in unknown executor with exit code %v and error %v", exitCode, err)
546+
}
547+
}

libs/execution/terragrunt.go

+52-21
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@ import (
44
"bytes"
55
"fmt"
66
"io"
7+
"log"
78
"os"
89
"os/exec"
10+
"strings"
911
)
1012

1113
type Terragrunt struct {
1214
WorkingDir string
1315
}
1416

1517
func (terragrunt Terragrunt) Init(params []string, envs map[string]string) (string, string, error) {
16-
return terragrunt.runTerragruntCommand("init", true, envs, params...)
18+
19+
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("init", true, envs, params...)
20+
if exitCode != 0 {
21+
logCommandFail(exitCode, err)
22+
}
1723

24+
return stdout, stderr, err
1825
}
1926

2027
func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[string]string) (string, string, error) {
@@ -23,41 +30,58 @@ func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[strin
2330
if plan != nil {
2431
params = append(params, *plan)
2532
}
26-
stdout, stderr, err := terragrunt.runTerragruntCommand("apply", true, envs, params...)
33+
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("apply", true, envs, params...)
34+
if exitCode != 0 {
35+
logCommandFail(exitCode, err)
36+
}
37+
2738
return stdout, stderr, err
2839
}
2940

3041
func (terragrunt Terragrunt) Destroy(params []string, envs map[string]string) (string, string, error) {
3142
params = append(params, "--auto-approve")
3243
params = append(params, "--terragrunt-non-interactive")
33-
stdout, stderr, err := terragrunt.runTerragruntCommand("destroy", true, envs, params...)
44+
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("destroy", true, envs, params...)
45+
if exitCode != 0 {
46+
logCommandFail(exitCode, err)
47+
}
48+
49+
3450
return stdout, stderr, err
3551
}
3652

3753
func (terragrunt Terragrunt) Plan(params []string, envs map[string]string) (bool, string, string, error) {
38-
stdout, stderr, err := terragrunt.runTerragruntCommand("plan", true, envs, params...)
54+
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("plan", true, envs, params...)
55+
if exitCode != 0 {
56+
logCommandFail(exitCode, err)
57+
}
58+
3959
return true, stdout, stderr, err
4060
}
4161

4262
func (terragrunt Terragrunt) Show(params []string, envs map[string]string) (string, string, error) {
43-
stdout, stderr, err := terragrunt.runTerragruntCommand("show", false, envs, params...)
63+
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("show", false, envs, params...)
64+
if exitCode != 0 {
65+
logCommandFail(exitCode, err)
66+
}
67+
4468
return stdout, stderr, err
4569
}
4670

47-
func (terragrunt Terragrunt) runTerragruntCommand(command string, printOutputToStdout bool, envs map[string]string, arg ...string) (string, string, error) {
71+
func (terragrunt Terragrunt) runTerragruntCommand(command string, printOutputToStdout bool, envs map[string]string, arg ...string) (stdOut string, stdErr string, exitCode int, err error) {
4872
args := []string{command}
4973
args = append(args, arg...)
50-
cmd := exec.Command("terragrunt", args...)
51-
cmd.Dir = terragrunt.WorkingDir
52-
53-
env := os.Environ()
54-
env = append(env, "TF_CLI_ARGS=-no-color")
55-
env = append(env, "TF_IN_AUTOMATION=true")
5674

57-
for k, v := range envs {
58-
env = append(env, fmt.Sprintf("%s=%s", k, v))
75+
expandedArgs := make([]string, 0)
76+
for _, p := range args {
77+
s := os.ExpandEnv(p)
78+
s = strings.TrimSpace(s)
79+
if s != "" {
80+
expandedArgs = append(expandedArgs, s)
81+
}
5982
}
6083

84+
// Set up common output buffers
6185
var mwout, mwerr io.Writer
6286
var stdout, stderr bytes.Buffer
6387
if printOutputToStdout {
@@ -68,15 +92,22 @@ func (terragrunt Terragrunt) runTerragruntCommand(command string, printOutputToS
6892
mwerr = io.Writer(&stderr)
6993
}
7094

71-
cmd.Env = env
72-
cmd.Stdout = mwout
73-
cmd.Stderr = mwerr
95+
cmd := exec.Command("terragrunt", args...)
96+
log.Printf("Running command: terragrunt %v", expandedArgs)
97+
cmd.Dir = terragrunt.WorkingDir
7498

75-
err := cmd.Run()
99+
env := os.Environ()
100+
env = append(env, "TF_CLI_ARGS=-no-color")
101+
env = append(env, "TF_IN_AUTOMATION=true")
76102

77-
if err != nil {
78-
return stdout.String(), stderr.String(), fmt.Errorf("error: %v", err)
103+
for k, v := range envs {
104+
env = append(env, fmt.Sprintf("%s=%s", k, v))
79105
}
80106

81-
return stdout.String(), stderr.String(), err
107+
cmd.Env = env
108+
cmd.Stdout = mwout
109+
cmd.Stderr = mwerr
110+
111+
err = cmd.Run()
112+
return stdout.String(), stderr.String(), cmd.ProcessState.ExitCode(), err
82113
}

libs/scheduler/aws.go

+36-16
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,34 @@ func (job *Job) PopulateAwsCredentialsEnvVarsForJob() error {
4545
log.Printf("Project-level AWS role detected, Assuming role for project: %v", job.ProjectName)
4646
var err error
4747
backendConfigArgs, err := populateretrieveBackendConfigArgs(*job.StateEnvProvider)
48-
if err != nil {
49-
log.Printf("Failed to get keys from role: %v", err)
50-
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
51-
}
5248

53-
if job.PlanStage != nil {
54-
// TODO: check that the first step is infact the terraform "init" step
55-
job.PlanStage.Steps[0].ExtraArgs = append(job.PlanStage.Steps[0].ExtraArgs, backendConfigArgs...)
56-
}
57-
if job.ApplyStage != nil {
58-
// TODO: check that the first step is infact the terraform "init" step
59-
job.ApplyStage.Steps[0].ExtraArgs = append(job.ApplyStage.Steps[0].ExtraArgs, backendConfigArgs...)
60-
}
61-
if err != nil {
62-
log.Printf("Failed to get keys from role: %v", err)
63-
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
49+
// Terragrunt will cause a backend configuration problem if backend-config options are passed and envs of the same key are passed.
50+
// which will trigger a request to init with --reconfigure, so do not use backend-config for terragrunt
51+
if job.Terragrunt != true {
52+
if err != nil {
53+
log.Printf("Failed to get keys from role: %v", err)
54+
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
55+
}
56+
57+
if job.PlanStage != nil {
58+
// TODO: check that the first step is infact the terraform "init" step
59+
job.PlanStage.Steps[0].ExtraArgs = append(job.PlanStage.Steps[0].ExtraArgs, backendConfigArgs...)
60+
}
61+
if job.ApplyStage != nil {
62+
// TODO: check that the first step is infact the terraform "init" step
63+
job.ApplyStage.Steps[0].ExtraArgs = append(job.ApplyStage.Steps[0].ExtraArgs, backendConfigArgs...)
64+
}
65+
if err != nil {
66+
log.Printf("Failed to get keys from role: %v", err)
67+
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
68+
}
69+
} else {
70+
job.StateEnvVars, err = populateKeys(job.StateEnvVars, *job.StateEnvProvider)
71+
if err != nil {
72+
log.Printf("Failed to get keys from role (StateEnvProvider): %v", err)
73+
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
74+
}
6475
}
65-
6676
}
6777

6878
if job.CommandEnvProvider != nil {
@@ -73,6 +83,16 @@ func (job *Job) PopulateAwsCredentialsEnvVarsForJob() error {
7383
return fmt.Errorf("Failed to get (command) keys from role: %v", err)
7484
}
7585
}
86+
87+
// If state environment variables are not set them to match command env vars
88+
if len(job.StateEnvVars) == 0 && len(job.CommandEnvVars) != 0 {
89+
job.StateEnvVars = job.CommandEnvVars
90+
}
91+
92+
if len(job.StateEnvVars) != 0 && len(job.CommandEnvVars) == 0 {
93+
job.CommandEnvVars = job.StateEnvVars
94+
}
95+
7696
return nil
7797
}
7898

0 commit comments

Comments
 (0)