Skip to content

Commit

Permalink
Fixes issues with AWSAssumeRole in Blocks for Terraform being passed …
Browse files Browse the repository at this point in the history
…in (#1720)

* Fixes issues with AWSAssumeRole in Blocks for Terraform being passed in
  • Loading branch information
ben-of-codecraft authored Sep 23, 2024
1 parent b4bc8f3 commit 0720b5b
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 39 deletions.
12 changes: 10 additions & 2 deletions libs/digger_config/digger_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,22 @@ func HandleYamlProjectGeneration(config *DiggerConfigYaml, terraformDir string,
workflow = b.Workflow
}

err := hydrateDiggerConfigYamlWithTerragrunt(config, TerragruntParsingConfig{
tgParsingConfig := TerragruntParsingConfig{
CreateProjectName: true,
DefaultWorkflow: workflow,
WorkflowFile: b.WorkflowFile,
FilterPath: path.Join(terraformDir, *b.RootDir),
}, terraformDir)
};

// allow blocks to pass in roles that can be assummed by aws
tgParsingConfig.AwsRoleToAssume = b.AwsRoleToAssume


err := hydrateDiggerConfigYamlWithTerragrunt(config, tgParsingConfig, terraformDir)
if err != nil {
return err
}

}
} else {
includePatterns = []string{b.Include}
Expand Down Expand Up @@ -500,6 +507,7 @@ func hydrateDiggerConfigYamlWithTerragrunt(configYaml *DiggerConfigYaml, parsing
WorkflowFile: &workflowFile,
IncludePatterns: atlantisProject.Autoplan.WhenModified,
Generated: true,
AwsRoleToAssume: parsingConfig.AwsRoleToAssume,
})
}
return nil
Expand Down
1 change: 1 addition & 0 deletions libs/digger_config/yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ type TerragruntParsingConfig struct {
UseProjectMarkers bool `yaml:"useProjectMarkers"`
ExecutionOrderGroups *bool `yaml:"executionOrderGroups"`
WorkflowFile string `yaml:"workflow_file"`
AwsRoleToAssume *AssumeRoleForProjectConfig `yaml:"aws_role_to_assume,omitempty"`
}

func (p *ProjectYaml) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expand Down
14 changes: 14 additions & 0 deletions libs/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"path"
"regexp"
"runtime"
"strconv"
"strings"

Expand Down Expand Up @@ -217,6 +218,7 @@ func (d DiggerExecutor) Plan() (*terraform_utils.TerraformSummary, bool, bool, s
}
}
for _, step := range planSteps {
log.Printf(" Running step: %v\n", step.Action)
if step.Action == "init" {
_, stderr, err := d.TerraformExecutor.Init(step.ExtraArgs, d.StateEnvVars)
if err != nil {
Expand Down Expand Up @@ -531,3 +533,15 @@ func cleanupTerraformPlan(nonEmptyPlan bool, planError error, stdout string, std
func (d DiggerExecutor) projectId() string {
return d.ProjectNamespace + "#" + d.ProjectName
}

// this will log an exit code and error based on the executor of the executor drivers are by filename
func logCommandFail(exitCode int, err error) {

_, filename, _, ok := runtime.Caller(1);
if ok {
executor := strings.TrimSuffix(path.Base(filename), path.Ext(filename))
log.Printf("Command failed in %v with exit code %v and error %v", executor, exitCode, err)
} else {
log.Printf("Command failed in unknown executor with exit code %v and error %v", exitCode, err)
}
}
73 changes: 52 additions & 21 deletions libs/execution/terragrunt.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@ import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
)

type Terragrunt struct {
WorkingDir string
}

func (terragrunt Terragrunt) Init(params []string, envs map[string]string) (string, string, error) {
return terragrunt.runTerragruntCommand("init", true, envs, params...)

stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("init", true, envs, params...)
if exitCode != 0 {
logCommandFail(exitCode, err)
}

return stdout, stderr, err
}

func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[string]string) (string, string, error) {
Expand All @@ -23,41 +30,58 @@ func (terragrunt Terragrunt) Apply(params []string, plan *string, envs map[strin
if plan != nil {
params = append(params, *plan)
}
stdout, stderr, err := terragrunt.runTerragruntCommand("apply", true, envs, params...)
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("apply", true, envs, params...)
if exitCode != 0 {
logCommandFail(exitCode, err)
}

return stdout, stderr, err
}

func (terragrunt Terragrunt) Destroy(params []string, envs map[string]string) (string, string, error) {
params = append(params, "--auto-approve")
params = append(params, "--terragrunt-non-interactive")
stdout, stderr, err := terragrunt.runTerragruntCommand("destroy", true, envs, params...)
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("destroy", true, envs, params...)
if exitCode != 0 {
logCommandFail(exitCode, err)
}


return stdout, stderr, err
}

func (terragrunt Terragrunt) Plan(params []string, envs map[string]string) (bool, string, string, error) {
stdout, stderr, err := terragrunt.runTerragruntCommand("plan", true, envs, params...)
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("plan", true, envs, params...)
if exitCode != 0 {
logCommandFail(exitCode, err)
}

return true, stdout, stderr, err
}

func (terragrunt Terragrunt) Show(params []string, envs map[string]string) (string, string, error) {
stdout, stderr, err := terragrunt.runTerragruntCommand("show", false, envs, params...)
stdout, stderr, exitCode, err := terragrunt.runTerragruntCommand("show", false, envs, params...)
if exitCode != 0 {
logCommandFail(exitCode, err)
}

return stdout, stderr, err
}

func (terragrunt Terragrunt) runTerragruntCommand(command string, printOutputToStdout bool, envs map[string]string, arg ...string) (string, string, error) {
func (terragrunt Terragrunt) runTerragruntCommand(command string, printOutputToStdout bool, envs map[string]string, arg ...string) (stdOut string, stdErr string, exitCode int, err error) {
args := []string{command}
args = append(args, arg...)
cmd := exec.Command("terragrunt", args...)
cmd.Dir = terragrunt.WorkingDir

env := os.Environ()
env = append(env, "TF_CLI_ARGS=-no-color")
env = append(env, "TF_IN_AUTOMATION=true")

for k, v := range envs {
env = append(env, fmt.Sprintf("%s=%s", k, v))
expandedArgs := make([]string, 0)
for _, p := range args {
s := os.ExpandEnv(p)
s = strings.TrimSpace(s)
if s != "" {
expandedArgs = append(expandedArgs, s)
}
}

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

cmd.Env = env
cmd.Stdout = mwout
cmd.Stderr = mwerr
cmd := exec.Command("terragrunt", args...)
log.Printf("Running command: terragrunt %v", expandedArgs)
cmd.Dir = terragrunt.WorkingDir

err := cmd.Run()
env := os.Environ()
env = append(env, "TF_CLI_ARGS=-no-color")
env = append(env, "TF_IN_AUTOMATION=true")

if err != nil {
return stdout.String(), stderr.String(), fmt.Errorf("error: %v", err)
for k, v := range envs {
env = append(env, fmt.Sprintf("%s=%s", k, v))
}

return stdout.String(), stderr.String(), err
cmd.Env = env
cmd.Stdout = mwout
cmd.Stderr = mwerr

err = cmd.Run()
return stdout.String(), stderr.String(), cmd.ProcessState.ExitCode(), err
}
52 changes: 36 additions & 16 deletions libs/scheduler/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,34 @@ func (job *Job) PopulateAwsCredentialsEnvVarsForJob() error {
log.Printf("Project-level AWS role detected, Assuming role for project: %v", job.ProjectName)
var err error
backendConfigArgs, err := populateretrieveBackendConfigArgs(*job.StateEnvProvider)
if err != nil {
log.Printf("Failed to get keys from role: %v", err)
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
}

if job.PlanStage != nil {
// TODO: check that the first step is infact the terraform "init" step
job.PlanStage.Steps[0].ExtraArgs = append(job.PlanStage.Steps[0].ExtraArgs, backendConfigArgs...)
}
if job.ApplyStage != nil {
// TODO: check that the first step is infact the terraform "init" step
job.ApplyStage.Steps[0].ExtraArgs = append(job.ApplyStage.Steps[0].ExtraArgs, backendConfigArgs...)
}
if err != nil {
log.Printf("Failed to get keys from role: %v", err)
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
// Terragrunt will cause a backend configuration problem if backend-config options are passed and envs of the same key are passed.
// which will trigger a request to init with --reconfigure, so do not use backend-config for terragrunt
if job.Terragrunt != true {
if err != nil {
log.Printf("Failed to get keys from role: %v", err)
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
}

if job.PlanStage != nil {
// TODO: check that the first step is infact the terraform "init" step
job.PlanStage.Steps[0].ExtraArgs = append(job.PlanStage.Steps[0].ExtraArgs, backendConfigArgs...)
}
if job.ApplyStage != nil {
// TODO: check that the first step is infact the terraform "init" step
job.ApplyStage.Steps[0].ExtraArgs = append(job.ApplyStage.Steps[0].ExtraArgs, backendConfigArgs...)
}
if err != nil {
log.Printf("Failed to get keys from role: %v", err)
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
}
} else {
job.StateEnvVars, err = populateKeys(job.StateEnvVars, *job.StateEnvProvider)
if err != nil {
log.Printf("Failed to get keys from role (StateEnvProvider): %v", err)
return fmt.Errorf("Failed to get (state) keys from role: %v", err)
}
}

}

if job.CommandEnvProvider != nil {
Expand All @@ -73,6 +83,16 @@ func (job *Job) PopulateAwsCredentialsEnvVarsForJob() error {
return fmt.Errorf("Failed to get (command) keys from role: %v", err)
}
}

// If state environment variables are not set them to match command env vars
if len(job.StateEnvVars) == 0 && len(job.CommandEnvVars) != 0 {
job.StateEnvVars = job.CommandEnvVars
}

if len(job.StateEnvVars) != 0 && len(job.CommandEnvVars) == 0 {
job.CommandEnvVars = job.StateEnvVars
}

return nil
}

Expand Down

0 comments on commit 0720b5b

Please sign in to comment.