Skip to content

Commit

Permalink
Merge pull request #153 from synfinatic/default-region
Browse files Browse the repository at this point in the history
Rework how we manage the AWS_DEFAULT_REGION
  • Loading branch information
synfinatic authored Nov 20, 2021
2 parents 570b7b7 + b96dc6a commit 9758cec
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 68 deletions.
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ Suggested use (bash): `eval $(aws-sso eval <args>)`

Flags:

* `--region <region>`, `-r` -- Specify the `$AWS_DEFAULT_REGION` to use
* `--arn <arn>`, `-a` -- ARN of role to assume (`$AWS_SSO_ROLE_ARN`)
* `--account <account>`, `-A` -- AWS AccountID of role to assume (`$AWS_SSO_ACCOUNTID`)
* `--duration <minutes>`, `-d` -- AWS Session duration in minutes (default 60) (`$AWS_SSO_DURATION`)
* `--role <role>`, `-R` -- Name of AWS Role to assume (requires `--account`) (`$AWS_SSO_ROLE`)
* `--no-region` -- Do not set the AWS_DEFAULT_REGION from config

**Note:** The `eval` command will never honor the `--url-action=print`
option as this will intefere with bash/zsh/etc ability to evaluate
Expand All @@ -167,12 +167,12 @@ commands.

Flags:

* `--region <region>, `-r` -- Specify the `$AWS_DEFAULT_REGION` to use
* `--arn <arn>`, `-a` -- ARN of role to assume (`$AWS_SSO_ROLE_ARN`)
* `--account <account>`, `-A` -- AWS AccountID of role to assume (`$AWS_SSO_ACCOUNTID`)
* `--duration <minutes>`, `-d` -- AWS Session duration in minutes (default 60) (`$AWS_SSO_DURATION`)
* `--env`, `-e` -- Use existing ENV vars generated by AWS SSO to generate a URL
* `--role <role>`, `-R` -- Name of AWS Role to assume (requires `--account`) (`$AWS_SSO_ROLE`)
* `--no-region` -- Do not set the AWS_DEFAULT_REGION from config

Arguments: `[<command>] [<args> ...]`

Expand Down Expand Up @@ -276,6 +276,7 @@ SSOConfig:
<Name of AWS SSO>:
SSORegion: <AWS Region where AWS SSO is deployed>
StartUrl: <URL for AWS SSO Portal>
DefaultRegion: <AWS_DEFAULT_REGION>
Accounts: # optional block for specifying tags & overrides
<AccountId>:
Name: <Friendly Name of Account>
Expand Down Expand Up @@ -330,6 +331,24 @@ SSO provider (Okta/OneLogin/etc).

The `StartUrl` is required.

### SSORegion

Each AWS SSO instance is configured in a specific AWS region which needs to be set here.

The `SSORegion` is required.

### DefaultRegion

The `DefaultRegion` allows you to define a value for the `$AWS_DEFAULT_REGION` when switching to a role.
Note that, aws-sso will NEVER change an existing `$AWS_DEFAULT_REGION` set by the user.

`DefaultRegion` can be specified at the following levels and the first match is selected:

1. `SSOConfig -> <Name of the AWS SSO> -> Accounts -> <AccountId> -> Roles -> <RoleName>`
1. `SSOConfig -> <Name of the AWS SSO> -> Accounts -> <AccountId>`
1. `SSOConfig -> <Name of AWS SSO>`
1. Top level of the file

### Accounts

The `Accounts` block is completely optional! The only purpose of this block
Expand Down
31 changes: 18 additions & 13 deletions cmd/console_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
const AWS_FEDERATED_URL = "https://signin.aws.amazon.com/federation"

type ConsoleCmd struct {
// Console actually should honor the --region flag
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
UseEnv bool `kong:"short='e',help='Use existing ENV vars to generate URL'"`
Expand All @@ -59,41 +60,38 @@ func (cc *ConsoleCmd) Run(ctx *RunContext) error {
return err
}

region := ctx.Settings.GetDefaultRegion(accountid, role)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
return openConsole(ctx, awssso, accountid, role, region)
return openConsole(ctx, awssso, accountid, role)
} else if ctx.Cli.Console.AccountId != 0 || ctx.Cli.Console.Role != "" {
if ctx.Cli.Console.AccountId == 0 || ctx.Cli.Console.Role == "" {
return fmt.Errorf("Please specify both --account and --role")
}
awssso := doAuth(ctx)
region := ctx.Settings.GetDefaultRegion(ctx.Cli.Console.AccountId, ctx.Cli.Console.Role)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
return openConsole(ctx, awssso, ctx.Cli.Console.AccountId, ctx.Cli.Console.Role, region)
return openConsole(ctx, awssso, ctx.Cli.Console.AccountId, ctx.Cli.Console.Role)
} else if ctx.Cli.Console.UseEnv {
if ctx.Cli.Console.AccessKeyId == "" {
return fmt.Errorf("AWS_ACCESS_KEY_ID is not set")
}

if ctx.Cli.Console.SecretAccessKey == "" {
return fmt.Errorf("AWS_SECRET_ACCESS_KEY is not set")
}

if ctx.Cli.Console.SessionToken == "" {
return fmt.Errorf("AWS_SESSION_TOKEN is not set")
}

creds := storage.RoleCredentials{
AccessKeyId: ctx.Cli.Console.AccessKeyId,
SecretAccessKey: ctx.Cli.Console.SecretAccessKey,
SessionToken: ctx.Cli.Console.SessionToken,
}

accountid, err := strconv.ParseInt(os.Getenv("AWS_ACCOUNT_ID"), 10, 64)
if err != nil {
return fmt.Errorf("Unable to parse AWS_ACCOUNT_ID: %s", os.Getenv("AWS_ACCOUNT_ID"))
}
region := ctx.Settings.GetDefaultRegion(accountid, os.Getenv("AWS_ROLE_NAME"))

region := ctx.Settings.GetDefaultRegion(accountid, os.Getenv("AWS_ROLE_NAME"), false)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}
Expand Down Expand Up @@ -124,7 +122,12 @@ func (cc *ConsoleCmd) Run(ctx *RunContext) error {
}

// opens the AWS console or just prints the URL
func openConsole(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, region string) error {
func openConsole(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role string) error {
region := ctx.Settings.GetDefaultRegion(accountid, role, false)
if ctx.Cli.Console.Region != "" {
region = ctx.Cli.Console.Region
}

ctx.Settings.Cache.AddHistory(utils.MakeRoleARN(accountid, role), ctx.Settings.HistoryLimit)
if err := ctx.Settings.Cache.Save(false); err != nil {
log.WithError(err).Warnf("Unable to update cache")
Expand All @@ -149,19 +152,21 @@ func openConsoleAccessKey(ctx *RunContext, creds *storage.RoleCredentials, durat
if err != nil {
return fmt.Errorf("Unable to login to AWS: %s", err.Error())
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

loginResponse := LoginResponse{}
err = json.Unmarshal(body, &loginResponse)
if err != nil {
return fmt.Errorf("Error parsing Login response: %s", err.Error())
}

login := LoginUrlParams{
Issuer: "https://github.com/synfinatic/aws-sso-cli", // FIXME
Issuer: "https://github.com/synfinatic/aws-sso-cli",
Destination: fmt.Sprintf("https://console.aws.amazon.com/console/home?region=%s", region),
SigninToken: loginResponse.SigninToken,
}
Expand Down
22 changes: 13 additions & 9 deletions cmd/eval_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ import (

type EvalCmd struct {
// AWS Params
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn',xor='arn-1',xor='arn-2'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNTID',predictor='accountId',xor='arn-1'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_ROLE_NAME',predictor='role',xor='arn-2'"`
Clear bool `kong:"short='c',help='Generate \"unset XXXX\" commands to clear environment'"`
NoRegion bool `kong:"help='Do not set/clear AWS_DEFAULT_REGION from config'"`
}

func (cc *EvalCmd) Run(ctx *RunContext) error {
var err error

if ctx.Cli.Eval.Clear {
unsetEnvVars()
unsetEnvVars(ctx)
return nil
}

Expand All @@ -54,7 +54,7 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
// if CLI args are speecified, use that
role := ctx.Cli.Eval.Role
account := ctx.Cli.Eval.AccountId
region := ctx.Cli.Eval.Region
region := ctx.Settings.GetDefaultRegion(account, role, false)

if len(ctx.Cli.Eval.Arn) > 0 {
account, role, err = utils.ParseRoleARN(ctx.Cli.Eval.Arn)
Expand All @@ -78,10 +78,6 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
log.Infof("Refreshing current AWS Role credentials")
}

if len(region) == 0 {
region = ctx.Settings.GetDefaultRegion(account, role)
}

awssso := doAuth(ctx)
for k, v := range execShellEnvs(ctx, awssso, account, role, region) {
if strings.Contains(v, " ") {
Expand All @@ -93,7 +89,7 @@ func (cc *EvalCmd) Run(ctx *RunContext) error {
return nil
}

func unsetEnvVars() {
func unsetEnvVars(ctx *RunContext) {
envs := []string{
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
Expand All @@ -102,9 +98,17 @@ func unsetEnvVars() {
"AWS_ROLE_NAME",
"AWS_ROLE_ARN",
"AWS_SESSION_EXPIRATION",
"AWS_DEFAULT_REGION",
"AWS_SSO_PROFILE",
}

// clear the region if
// 1. User did not specify --no-region AND
// 2. The AWS_DEFAULT_REGION is managed by us (tracks AWS_SSO_DEFAULT_REGION)
if !ctx.Cli.Eval.NoRegion && os.Getenv("AWS_DEFAULT_REGION") == os.Getenv("AWS_SSO_DEFAULT_REGION") {
envs = append(envs, "AWS_DEFAULT_REGION")
envs = append(envs, "AWS_SSO_DEFAULT_REGION")
}

for _, e := range envs {
fmt.Printf("unset %s\n", e)
}
Expand Down
21 changes: 12 additions & 9 deletions cmd/exec_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import (

type ExecCmd struct {
// AWS Params
Region string `kong:"help='AWS Region',env='AWS_DEFAULT_REGION',predictor='region'"`
Duration int64 `kong:"short='d',help='AWS Session duration in minutes (default 60)',default=60,env='AWS_SSO_DURATION'"`
Arn string `kong:"short='a',help='ARN of role to assume',env='AWS_SSO_ROLE_ARN',predictor='arn',xor='arn-1',xor='arn-2'"`
AccountId int64 `kong:"name='account',short='A',help='AWS AccountID of role to assume',env='AWS_SSO_ACCOUNTID',predictor='accountId',xor='arn-1'"`
Role string `kong:"short='R',help='Name of AWS Role to assume',env='AWS_SSO_ROLE',predictor='role',xor='arn-2'"`
NoRegion bool `kong:"help='Do not set AWS_DEFAULT_REGION from config'"`

// Exec Params
Cmd string `kong:"arg,optional,name='command',help='Command to execute',env='SHELL'"`
Expand All @@ -62,16 +62,14 @@ func (cc *ExecCmd) Run(ctx *RunContext) error {
return err
}

region := ctx.Settings.GetDefaultRegion(accountid, role)
return execCmd(ctx, awssso, accountid, role, region)
return execCmd(ctx, awssso, accountid, role)
} else if ctx.Cli.Exec.AccountId != 0 || ctx.Cli.Exec.Role != "" {
if ctx.Cli.Exec.AccountId == 0 || ctx.Cli.Exec.Role == "" {
return fmt.Errorf("Please specify both --account and --role")
}
awssso := doAuth(ctx)

region := ctx.Settings.GetDefaultRegion(ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role)
return execCmd(ctx, awssso, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, region)
return execCmd(ctx, awssso, ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role)
}

// Nope, auto-complete mode...
Expand Down Expand Up @@ -125,7 +123,9 @@ func accountIdToStr(id int64) string {
}

// Executes Cmd+Args in the context of the AWS Role creds
func execCmd(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, region string) error {
func execCmd(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role string) error {
region := ctx.Settings.GetDefaultRegion(ctx.Cli.Exec.AccountId, ctx.Cli.Exec.Role, ctx.Cli.Exec.NoRegion)

ctx.Settings.Cache.AddHistory(utils.MakeRoleARN(accountid, role), ctx.Settings.HistoryLimit)
if err := ctx.Settings.Cache.Save(false); err != nil {
log.WithError(err).Warnf("Unable to update cache")
Expand Down Expand Up @@ -160,10 +160,13 @@ func execShellEnvs(ctx *RunContext, awssso *sso.AWSSSO, accountid int64, role, r
"AWS_ROLE_NAME": creds.RoleName,
"AWS_SESSION_EXPIRATION": creds.ExpireString(),
}
if ctx.Cli.Exec.Region != "" {
shellVars["AWS_DEFAULT_REGION"] = ctx.Cli.Exec.Region
} else if region != "" {

if len(region) > 0 {
shellVars["AWS_DEFAULT_REGION"] = region
shellVars["AWS_SSO_DEFAULT_REGION"] = region
} else {
// we no longer manage the region
shellVars["AWS_SSO_DEFAULT_REGION"] = ""
}

var profileFormat string = AwsSsoProfileTemplate
Expand Down
5 changes: 2 additions & 3 deletions cmd/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
"github.com/synfinatic/aws-sso-cli/utils"
)

type CompleterExec = func(*RunContext, *sso.AWSSSO, int64, string, string) error
type CompleterExec = func(*RunContext, *sso.AWSSSO, int64, string) error

type TagsCompleter struct {
ctx *RunContext
Expand Down Expand Up @@ -106,9 +106,8 @@ func (tc *TagsCompleter) Executor(args string) {
if err != nil {
log.Fatalf("Unable to parse %s: %s", roleArn, err.Error())
}
region := tc.ctx.Settings.GetDefaultRegion(aId, rName)
awsSSO := doAuth(tc.ctx)
err = tc.exec(tc.ctx, awsSSO, aId, rName, region)
err = tc.exec(tc.ctx, awsSSO, aId, rName)
if err != nil {
log.Fatalf("Unable to exec: %s", err.Error())
}
Expand Down
Loading

0 comments on commit 9758cec

Please sign in to comment.