From 0626a24968226580e42ede08c66516ff75fbdf1f Mon Sep 17 00:00:00 2001 From: Danny Schofield Date: Fri, 10 May 2024 12:57:39 -0400 Subject: [PATCH] resource: use sts.Creds instead of role In the management account if we try to assume the OrganizationAccountAccessRole and fail we should use the default credentials and assume they are in the management account. The OrganizationAccountAccessRole is created by default in child accounts, but not in the management account. --- resourceoperation/cdk.go | 52 +++++++++++++++++++++------------- resourceoperation/scp.go | 20 ++++++------- resourceoperation/terraform.go | 32 ++++++++++----------- 3 files changed, 59 insertions(+), 45 deletions(-) diff --git a/resourceoperation/cdk.go b/resourceoperation/cdk.go index fd5f164..485ddb4 100644 --- a/resourceoperation/cdk.go +++ b/resourceoperation/cdk.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sts" + "github.com/samsarahq/go/oops" "github.com/santiago-labs/telophasecli/cmd/runner" "github.com/santiago-labs/telophasecli/lib/awssess" "github.com/santiago-labs/telophasecli/lib/awssts" @@ -45,18 +46,18 @@ func (co *cdkOperation) ListDependents() []ResourceOperation { func (co *cdkOperation) Call(ctx context.Context) error { co.OutputUI.Print(fmt.Sprintf("Executing CDK stack in %s", co.Stack.Path), *co.Account) - opRole, region, err := authAWS(*co.Account, *co.Stack.RoleARN(*co.Account), co.OutputUI) + creds, region, err := authAWS(*co.Account, *co.Stack.RoleARN(*co.Account), co.OutputUI) if err != nil { return err } // We must bootstrap cdk with the account role. - bootstrapCDK := bootstrapCDK(opRole, region, *co.Account, co.Stack) + bootstrapCDK := bootstrapCDK(creds, region, *co.Account, co.Stack) if err := co.OutputUI.RunCmd(bootstrapCDK, *co.Account); err != nil { return err } - synthCDK := synthCDK(opRole, *co.Account, co.Stack) + synthCDK := synthCDK(creds, *co.Account, co.Stack) if err := co.OutputUI.RunCmd(synthCDK, *co.Account); err != nil { return err } @@ -76,11 +77,11 @@ func (co *cdkOperation) Call(ctx context.Context) error { cmd := exec.Command(localstack.CdkCmd(), cdkArgs...) cmd.Dir = co.Stack.Path - if opRole != nil { + if creds != nil { cmd.Env = awssts.SetEnviron(os.Environ(), - *opRole.Credentials.AccessKeyId, - *opRole.Credentials.SecretAccessKey, - *opRole.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, co.Stack.AWSRegionEnv(), ) } @@ -101,7 +102,7 @@ func (co *cdkOperation) ToString() string { return "" } -func bootstrapCDK(result *sts.AssumeRoleOutput, region string, acct resource.Account, stack resource.Stack) *exec.Cmd { +func bootstrapCDK(creds *sts.Credentials, region string, acct resource.Account, stack resource.Stack) *exec.Cmd { cdkArgs := append([]string{ "bootstrap", fmt.Sprintf("aws://%s/%s", acct.AccountID, region), @@ -111,11 +112,11 @@ func bootstrapCDK(result *sts.AssumeRoleOutput, region string, acct resource.Acc cmd := exec.Command(localstack.CdkCmd(), cdkArgs...) cmd.Dir = stack.Path - if result != nil { + if creds != nil { cmd.Env = awssts.SetEnviron(os.Environ(), - *result.Credentials.AccessKeyId, - *result.Credentials.SecretAccessKey, - *result.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, stack.AWSRegionEnv(), ) } @@ -123,7 +124,7 @@ func bootstrapCDK(result *sts.AssumeRoleOutput, region string, acct resource.Acc return cmd } -func synthCDK(result *sts.AssumeRoleOutput, acct resource.Account, stack resource.Stack) *exec.Cmd { +func synthCDK(creds *sts.Credentials, acct resource.Account, stack resource.Stack) *exec.Cmd { cdkArgs := append( []string{"synth"}, cdkDefaultArgs(acct, stack)..., @@ -131,11 +132,11 @@ func synthCDK(result *sts.AssumeRoleOutput, acct resource.Account, stack resourc cmd := exec.Command(localstack.CdkCmd(), cdkArgs...) cmd.Dir = stack.Path - if result != nil { + if creds != nil { cmd.Env = awssts.SetEnviron(os.Environ(), - *result.Credentials.AccessKeyId, - *result.Credentials.SecretAccessKey, - *result.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, stack.AWSRegionEnv(), ) } @@ -143,10 +144,20 @@ func synthCDK(result *sts.AssumeRoleOutput, acct resource.Account, stack resourc return cmd } -func authAWS(acct resource.Account, arn string, consoleUI runner.ConsoleUI) (*sts.AssumeRoleOutput, string, error) { +func authAWS(acct resource.Account, arn string, consoleUI runner.ConsoleUI) (*sts.Credentials, string, error) { + var svc *sts.STS sess := session.Must(awssess.DefaultSession()) svc = sts.New(sess) + // If we are in the management account we the OrganizationAccountAccessRole does not exist so fallback to the current credentials. + if acct.ManagementAccount { + output, err := svc.GetSessionToken(&sts.GetSessionTokenInput{}) + if err != nil { + return nil, "", oops.Wrapf(err, "GetSessionToken") + } + // TODO: figure out region for management account + return output.Credentials, "us-east-1", nil + } consoleUI.Print(fmt.Sprintf("Assuming role: %s", arn), acct) input := &sts.AssumeRoleInput{ @@ -155,7 +166,10 @@ func authAWS(acct resource.Account, arn string, consoleUI runner.ConsoleUI) (*st } role, err := awssess.AssumeRole(svc, input) - return role, *sess.Config.Region, err + if err != nil { + return nil, "", oops.Wrapf(err, "AssumeRole") + } + return role.Credentials, *sess.Config.Region, nil } func cdkDefaultArgs(acct resource.Account, stack resource.Stack) []string { diff --git a/resourceoperation/scp.go b/resourceoperation/scp.go index ff8fd32..8707ee5 100644 --- a/resourceoperation/scp.go +++ b/resourceoperation/scp.go @@ -105,10 +105,10 @@ func (so *scpOperation) ListDependents() []ResourceOperation { func (so *scpOperation) Call(ctx context.Context) error { so.OutputUI.Print(fmt.Sprintf("Executing SCP Terraform stack in %s", so.Stack.Path), *so.MgmtAcct) - var acctRole *sts.AssumeRoleOutput + var creds *sts.Credentials if so.MgmtAcct.AssumeRoleName != "" { var err error - acctRole, _, err = authAWS(*so.MgmtAcct, so.MgmtAcct.AssumeRoleARN(), so.OutputUI) + creds, _, err = authAWS(*so.MgmtAcct, so.MgmtAcct.AssumeRoleARN(), so.OutputUI) if err != nil { return err } @@ -121,11 +121,11 @@ func (so *scpOperation) Call(ctx context.Context) error { } if initTFCmd != nil { - if acctRole != nil { + if creds != nil { initTFCmd.Env = awssts.SetEnviron(os.Environ(), - *acctRole.Credentials.AccessKeyId, - *acctRole.Credentials.SecretAccessKey, - *acctRole.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, // SCPs can't have regions nil, ) @@ -150,11 +150,11 @@ func (so *scpOperation) Call(ctx context.Context) error { cmd := exec.Command(localstack.TfCmd(), args...) cmd.Dir = workingPath - if acctRole != nil { + if creds != nil { cmd.Env = awssts.SetEnviron(os.Environ(), - *acctRole.Credentials.AccessKeyId, - *acctRole.Credentials.SecretAccessKey, - *acctRole.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, // SCPs don't have regions nil, ) diff --git a/resourceoperation/terraform.go b/resourceoperation/terraform.go index dbab7cf..3769ae0 100644 --- a/resourceoperation/terraform.go +++ b/resourceoperation/terraform.go @@ -45,13 +45,13 @@ func (to *tfOperation) ListDependents() []ResourceOperation { func (to *tfOperation) Call(ctx context.Context) error { to.OutputUI.Print(fmt.Sprintf("Executing Terraform stack in %s", to.Stack.Path), *to.Account) - var stackRole *sts.AssumeRoleOutput + var creds *sts.Credentials var assumeRoleErr error if to.Account.AccountID != "" { if roleArn := to.Stack.RoleARN(*to.Account); roleArn != nil { - stackRole, _, assumeRoleErr = authAWS(*to.Account, *roleArn, to.OutputUI) + creds, _, assumeRoleErr = authAWS(*to.Account, *roleArn, to.OutputUI) } else { - stackRole, _, assumeRoleErr = authAWS(*to.Account, to.Account.AssumeRoleARN(), to.OutputUI) + creds, _, assumeRoleErr = authAWS(*to.Account, to.Account.AssumeRoleARN(), to.OutputUI) } if assumeRoleErr != nil { @@ -59,7 +59,7 @@ func (to *tfOperation) Call(ctx context.Context) error { } } - initTFCmd := to.initTf(stackRole) + initTFCmd := to.initTf(creds) if initTFCmd != nil { if err := to.OutputUI.RunCmd(initTFCmd, *to.Account); err != nil { return err @@ -67,7 +67,7 @@ func (to *tfOperation) Call(ctx context.Context) error { } // Set workspace if we are using it. - setWorkspace, err := to.setWorkspace(stackRole) + setWorkspace, err := to.setWorkspace(creds) if err != nil { return err } @@ -93,9 +93,9 @@ func (to *tfOperation) Call(ctx context.Context) error { cmd.Dir = workingPath cmd.Env = awssts.SetEnviron(os.Environ(), - *stackRole.Credentials.AccessKeyId, - *stackRole.Credentials.SecretAccessKey, - *stackRole.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, to.Stack.AWSRegionEnv(), ) @@ -112,7 +112,7 @@ func (to *tfOperation) Call(ctx context.Context) error { return nil } -func (to *tfOperation) initTf(role *sts.AssumeRoleOutput) *exec.Cmd { +func (to *tfOperation) initTf(creds *sts.Credentials) *exec.Cmd { workingPath := terraform.TmpPath(*to.Account, to.Stack.Path) terraformDir := filepath.Join(workingPath, ".terraform") if terraformDir == "" || !strings.Contains(terraformDir, "telophasedirs") { @@ -140,9 +140,9 @@ func (to *tfOperation) initTf(role *sts.AssumeRoleOutput) *exec.Cmd { cmd.Dir = workingPath cmd.Env = awssts.SetEnviron(os.Environ(), - *role.Credentials.AccessKeyId, - *role.Credentials.SecretAccessKey, - *role.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, to.Stack.AWSRegionEnv(), ) @@ -168,7 +168,7 @@ func replaceVals(workspace, AccountID, Region string) (string, error) { return currentContent, nil } -func (to *tfOperation) setWorkspace(role *sts.AssumeRoleOutput) (*exec.Cmd, error) { +func (to *tfOperation) setWorkspace(creds *sts.Credentials) (*exec.Cmd, error) { if !to.Stack.WorkspaceEnabled() { return nil, nil } @@ -184,9 +184,9 @@ func (to *tfOperation) setWorkspace(role *sts.AssumeRoleOutput) (*exec.Cmd, erro cmd.Dir = workingPath cmd.Env = awssts.SetEnviron(os.Environ(), - *role.Credentials.AccessKeyId, - *role.Credentials.SecretAccessKey, - *role.Credentials.SessionToken, + *creds.AccessKeyId, + *creds.SecretAccessKey, + *creds.SessionToken, to.Stack.AWSRegionEnv(), )