Skip to content

Commit

Permalink
[feature] added option for configuring AWS config by roles (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuannie1 authored Jun 16, 2020
1 parent 2d39662 commit 553c133
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 58 deletions.
11 changes: 6 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var rootCmd = &cobra.Command{
}
if verbose {
log.SetLevel(log.DebugLevel)
log.SetReportCaller(true)
}
err = configureLogrusHooks()
if err != nil {
Expand All @@ -72,11 +73,11 @@ func configureLogrusHooks() error {
logrus.FatalLevel,
logrus.ErrorLevel,
})
if err != nil {
logrus.Errorf("Error configuring Sentry")
return nil
}
log.AddHook(sentryHook)
if err != nil {
logrus.Errorf("Error configuring Sentry")
return nil
}
log.AddHook(sentryHook)
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.14

require (
github.com/AlecAivazis/survey/v2 v2.0.7
github.com/aws/aws-sdk-go v1.31.15
github.com/aws/aws-sdk-go v1.32.1
github.com/blang/semver v3.5.1+incompatible
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 // indirect
github.com/chanzuckerberg/go-misc v0.0.0-20200611000103-3caf6f173497
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aws/aws-lambda-go v1.16.0/go.mod h1:FEwgPLE6+8wcGBTe5cJN3JWurd1Ztm9zN4jsXsjzKKw=
github.com/aws/aws-sdk-go v1.30.20/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.31.15 h1:1Ahi6nvJLg5cjO5i3U7BWh91/zOw//tqOTLpLnIeyss=
github.com/aws/aws-sdk-go v1.31.15/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.32.1 h1:0dy5DkMKNPH9mLWveAWA9ZTiKIEEvJJA6fbe0eCs19k=
github.com/aws/aws-sdk-go v1.32.1/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
Expand Down
152 changes: 118 additions & 34 deletions pkg/aws_config_client/completer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/AlecAivazis/survey/v2"
server "github.com/chanzuckerberg/aws-oidc/pkg/aws_config_server"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gopkg.in/ini.v1"
)

Expand Down Expand Up @@ -42,7 +43,7 @@ func (c *completer) getAccountOptions(accounts []server.AWSAccount) []string {
func (c *completer) getRoleOptions(profiles []server.AWSProfile) []string {
roleOptions := []string{}
for _, profile := range profiles {
roleOptions = append(roleOptions, profile.RoleARN)
roleOptions = append(roleOptions, profile.RoleName)
}

return roleOptions
Expand Down Expand Up @@ -117,57 +118,140 @@ func (c *completer) SurveyProfile() (*AWSNamedProfile, error) {
return namedProfile, nil
}

// SurveyRole will ask a user to configure a default role
func (c *completer) SurveyRoles() ([]*AWSNamedProfile, error) {
// first prompt for roles
roles := c.awsConfig.GetRoleNames()
accounts := c.awsConfig.GetAccounts()

roleIdx, err := c.prompt.Select(
"Select the AWS role you would like to make default:",
roles,
)
if err != nil {
return nil, err
}
targetRole := roles[roleIdx]

configuredProfiles := []*AWSNamedProfile{}

for _, account := range accounts {
profileName := c.calculateDefaultProfileName(account)

// get the roles associated with this account
profiles := c.awsConfig.GetProfilesForAccount(account)
for _, profile := range profiles {

// Initialize a new AWSNamedProfile
currentProfile := AWSNamedProfile{
AWSProfile: server.AWSProfile{
ClientID: profile.ClientID,
AWSAccount: profile.AWSAccount,
RoleARN: profile.RoleARN,
IssuerURL: profile.IssuerURL,
},
}

currentProfile.Name = fmt.Sprintf("%s-%s", profileName, profile.RoleName)
configuredProfiles = append(configuredProfiles, &currentProfile)

if profile.RoleName == targetRole {
defaultProfile := AWSNamedProfile{
Name: profileName,
AWSProfile: server.AWSProfile{
ClientID: profile.ClientID,
AWSAccount: profile.AWSAccount,
RoleARN: profile.RoleARN,
IssuerURL: profile.IssuerURL,
},
}
configuredProfiles = append(configuredProfiles, &defaultProfile)
}
}
}
return configuredProfiles, nil
}

func (c *completer) SurveyProfiles() ([]*AWSNamedProfile, error) {
collectedProfiles := []*AWSNamedProfile{}
for {
currentProfile, err := c.SurveyProfile()
if err != nil {
return nil, err
}
collectedProfiles = append(collectedProfiles, currentProfile)
cnt, err := c.Continue()
if err != nil {
return nil, err
}
if !cnt {
break
}
}
return collectedProfiles, nil
}

func (c *completer) Survey() ([]*AWSNamedProfile, error) {
configureOptions := []string{
"Automatically configure the same role for each account?",
"Configure one role at a time?"}
configureFuncs := []func() ([]*AWSNamedProfile, error){c.SurveyRoles, c.SurveyProfiles}
configureIdx, err := c.prompt.Select("How would you like to configure your AWS config?", configureOptions)
if err != nil {
return nil, err
}
return configureFuncs[configureIdx]()
}

func (c *completer) Continue() (bool, error) {
return c.prompt.Confirm("Would you like to configure another profile?", true)
}

func (c *completer) writeAWSProfile(out *ini.File, region string, profile *AWSNamedProfile) error {
profileSection := fmt.Sprintf("profile %s", profile.Name)
func (c *completer) writeAWSProfiles(out *ini.File, region string, profiles []*AWSNamedProfile) error {

credsProcessValue := fmt.Sprintf(
"sh -c 'aws-oidc creds-process --issuer-url=%s --client-id=%s --aws-role-arn=%s 2> /dev/tty'",
profile.AWSProfile.IssuerURL,
profile.AWSProfile.ClientID,
profile.AWSProfile.RoleARN,
)
for _, profile := range profiles {
profileSection := fmt.Sprintf("profile %s", profile.Name)

// First delete sections with this name so old configuration doesn't persist
out.DeleteSection(profileSection)
section, err := out.NewSection(profileSection)
if err != nil {
return errors.Wrapf(err, "Unable to create %s section in AWS Config", profileSection)
credsProcessValue := fmt.Sprintf(
"sh -c 'aws-oidc creds-process --issuer-url=%s --client-id=%s --aws-role-arn=%s 2> /dev/tty'",
profile.AWSProfile.IssuerURL,
profile.AWSProfile.ClientID,
profile.AWSProfile.RoleARN,
)

// First delete sections with this name so old configuration doesn't persist
out.DeleteSection(profileSection)
section, err := out.NewSection(profileSection)
if err != nil {
return errors.Wrapf(err, "Unable to create %s section in AWS Config", profileSection)
}
section.Key(AWSConfigSectionOutput).SetValue("json")
section.Key(AWSConfigSectionCredentialProcess).SetValue(credsProcessValue)
section.Key(AWSConfigSectionRegion).SetValue(region)
}
section.Key(AWSConfigSectionOutput).SetValue("json")
section.Key(AWSConfigSectionCredentialProcess).SetValue(credsProcessValue)
section.Key(AWSConfigSectionRegion).SetValue(region)
return nil
}

func (c *completer) Loop(out *ini.File) error {
if len(c.awsConfig.Profiles) == 0 {
logrus.Info("You are not authorized for any roles. Please contact your AWS administrator if this is a mistake")
return nil
}

// assume same region for all accounts configured in this run?
region, err := c.SurveyRegion()
if err != nil {
return err
}

for {
profile, err := c.SurveyProfile()
if err != nil {
return err
}

err = c.writeAWSProfile(out, region, profile)
if err != nil {
return err
}
profiles, err := c.Survey()
if err != nil {
return err
}

cnt, err := c.Continue()
if err != nil {
return err
}
if !cnt {
break
}
err = c.writeAWSProfiles(out, region, profiles)
if err != nil {
return err
}
return nil
}
Loading

0 comments on commit 553c133

Please sign in to comment.