diff --git a/README.md b/README.md index eed0c99..c0f6676 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ state of the stack deployment. - Acceptance of "No updates to be performed." as a non-erroneous state - Environment Variable Substitution in Parameter and Tag files - YAML and JSON formatted stack policies +- Deploy using an assumed IAM role (often used to deploy stacks to other + accounts) More features are currently on the roadmap, which can be [found on Trello](https://trello.com/b/ECuGN86A) diff --git a/commands/deploy.go b/commands/deploy.go index 6421998..e5b71ef 100644 --- a/commands/deploy.go +++ b/commands/deploy.go @@ -69,7 +69,7 @@ var deployCmd = &cobra.Command{ } // Populate Stack ID - // Deliberately ignore errors here + // Deliberately ignore errors here, as the stack might not exist yet stack.GetStackInfo() after, err := stack.GetLastEventTime() @@ -90,9 +90,12 @@ var deployCmd = &cobra.Command{ } for { - // Refresh Stack State + refresh_stack_status: if err := stack.GetStackInfo(); err != nil { - log.Fatal(err) + if err2 := rotateRoleCredentials(err); err2 != nil { + log.Fatal(err) + } + goto refresh_stack_status } printStackEvents(&stack, after) diff --git a/commands/destroy.go b/commands/destroy.go index 7d457f4..4783d29 100644 --- a/commands/destroy.go +++ b/commands/destroy.go @@ -35,9 +35,12 @@ var destroyCmd = &cobra.Command{ } for { - // Refresh Stack State + refresh_stack_status: if err := stack.GetStackInfo(); err != nil { - log.Fatal(err) + if err2 := rotateRoleCredentials(err); err2 != nil { + log.Fatal(err) + } + goto refresh_stack_status } printStackEvents(&stack, after) diff --git a/commands/root.go b/commands/root.go index dfa9755..835c3ff 100644 --- a/commands/root.go +++ b/commands/root.go @@ -10,6 +10,7 @@ import ( forge "github.com/nathandines/forge/forgelib" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/spf13/cobra" ) @@ -60,9 +61,13 @@ func Execute() { } func printStackEvents(s *forge.Stack, after *time.Time) { +list_events: bunch, err := s.ListEvents(after) if err != nil { - log.Fatal(err) + if err2 := rotateRoleCredentials(err); err2 != nil { + log.Fatal(err) + } + goto list_events } for _, e := range bunch { // IDs renamed for JSON output to match the API response data @@ -91,3 +96,20 @@ func printStackEvents(s *forge.Stack, after *time.Time) { *after = *bunch[len(bunch)-1].Timestamp } } + +func rotateRoleCredentials(err error) error { + if awsErr, ok := err.(awserr.Error); ok && assumeRoleArn != "" { + switch awsErr.Code() { + case "ExpiredToken": + forge.UnassumeAllRoles() + if err2 := forge.AssumeRole(assumeRoleArn); err2 != nil { + return err + } + default: + return err + } + } else { + return err + } + return nil +} diff --git a/forgelib/auth.go b/forgelib/auth.go index 9b08a64..981ff42 100644 --- a/forgelib/auth.go +++ b/forgelib/auth.go @@ -35,6 +35,7 @@ func AssumeRole(roleArn string) error { return err } assumeOut, err := stsClient.AssumeRole(&sts.AssumeRoleInput{ + DurationSeconds: aws.Int64(900), RoleSessionName: aws.String(roleSessionName), RoleArn: aws.String(roleArn), })