Skip to content

Commit

Permalink
s/profile/role
Browse files Browse the repository at this point in the history
  • Loading branch information
Gary Zhu committed Jan 1, 2022
1 parent ee1266c commit f0d5596
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 44 deletions.
29 changes: 14 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
# aws-one-punch
One punch to grant all command prompts AWS access with IAM Role credentials in OSX.
One punch to grant all command prompts AWS access with IAM role credentials in OSX.

## Background ##
When working with micro services using Cloudformation, we normally open multiple command prompts so that we can update the corresponding AWS resources after changing each individual service. As recommended by AWS, we should use IAM roles instead of long-term access keys. But the pain point is that we will have to grant the access in each command prompt, or to update the local credentials file every time when the temporary credentials are expired.
When working with micro services using Cloudformation, we normally open multiple command prompts so that we can update the corresponding AWS resources after changing each individual service. As recommended by AWS, we should use IAM roles instead of long-term access keys in this case. But the pain point is that we will have to grant the access in each command prompt, or to update the local credentials file every time when the temporary credentials are expired.

## Solution ##
AWS-one-punch basically pulls all accounts and profiles(roles) with the SSO bearer token stored in cookie to generate new credentials and get them updated in the local credentials file which means with just one command, we can grant all command prompts the access.

**Note: for simplicity, the `default` profile will be used in the credentials file.**
AWS-one-punch basically pulls all accounts and IAM roles with the SSO bearer token stored in cookie to generate new credentials and get them updated in the local credentials file which means with just one command, we can grant all command prompts the access.

## Prerequisites ##
AWS CLI has been installed, and the default profile has been configured.
**Note: for simplicity, the `default` profile will be used in the credentials file.**

## Setup ##
1. install via Homebrew
```
brew install gaarazhu/aws-one-punch/aws-one-punch
```
2. set AWS management console domain in `~/.bash_profile` and reload it with `source ~/.bash_profile`
2. set AWS management console domain in `~/.bash_profile` or equivalent and reload it with `source ~/.bash_profile`
```
export AWS_CONSOLE_DOMAIN="garyz.awsapps.com"
```
3. run
```
$ aws-one-punch
NAME:
aws-one-punch - one punch to grant all command prompts AWS access in MacOS
aws-one-punch - one punch to grant all command prompts AWS access with IAM role credentials in OSX.
USAGE:
aws-one-punch [global options] command [command options] [arguments...]
Expand All @@ -35,8 +34,8 @@ VERSION:
COMMANDS:
list-accounts, ls-a List accounts
list-profiles, ls-p List profiles under an account
access, access Access AWS Resource with a profile
list-roles, ls-r List IAM roles under an account
access, a Access AWS Resource with IAM role credentials
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
Expand All @@ -48,7 +47,7 @@ GLOBAL OPTIONS:
1. list accounts
```
$ aws-one-punch list-accounts
2021/11/10 22:04:14 no AWS SSO Token found, please open the AWS Management Console https://gzhu.awsapps.com/start/#/ first
2021/11/10 22:04:14 No AWS SSO token found, please finish the SSO in the user portal first: https://gzhu.awsapps.com/start/#/ first
```

2. open the url, wait for the SSO finished and run above command again(PS. keep listing the accounts unitl it works as the token will only be written to local cookie after all resources have been loaded during the SSO)
Expand All @@ -61,15 +60,15 @@ AccountId: ins-siki23, accountName: 58868209 (Data Analytics)
AccountId: ins-14oasn, accountName: 66060440 (Shared Services)
```

3. list profiles
3. list IAM roles
```
$ aws-one-punch list-profiles --account-id ins-3sadfa
ProfileName: DigitalDeveloperNonprodAccess
$ aws-one-punch list-roles --account-id ins-3sadfa
RoleName: DigitalDeveloperNonprodAccess
```
4. one punch for access
```
$ aws-one-punch access --account-name 69127290 --profile-name DigitalDeveloperNonprodAccess
AWS access granted with account 69127290 and profile DigitalDeveloperNonprodAccess
$ aws-one-punch access --account-name 69127290 --role-name DigitalDeveloperNonprodAccess
AWS access granted with account 69127290 and IAM role DigitalDeveloperNonprodAccess
```

## Contribution ##
Expand Down
18 changes: 9 additions & 9 deletions aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ type accounts struct {
Result []account `json:"result"`
}

type profile struct {
type role struct {
Name string `json:"name"`
}

type profiles struct {
Result []profile `json:"result"`
type roles struct {
Result []role `json:"result"`
}

type credentials struct {
Expand Down Expand Up @@ -67,17 +67,17 @@ func (as *AWSService) getAccounts(url, token string) (accounts, error) {
return accounts, nil
}

func (as *AWSService) getProfiles(url, token string) (profiles, error) {
var profiles profiles
func (as *AWSService) getRoles(url, token string) (roles, error) {
var rs roles
bs, err := as.getAWSResource(url, token)
if err != nil {
return profiles, err
return rs, err
}
err = json.Unmarshal(bs, &profiles)
err = json.Unmarshal(bs, &rs)
if err != nil {
return profiles, err
return rs, err
}
return profiles, nil
return rs, nil
}

func (as *AWSService) getCredentials(url, token string) (credentials, error) {
Expand Down
10 changes: 5 additions & 5 deletions aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ func TestListAccounts(t *testing.T) {
}
}

func TestListProfiles(t *testing.T) {
func TestListRoles(t *testing.T) {
tt := []struct {
Body string
StatusCode int

Result profiles
Result roles
ErrorMessage string
}{
{
Expand All @@ -140,8 +140,8 @@ func TestListProfiles(t *testing.T) {
]
}`,
StatusCode: 200,
Result: profiles{
Result: []profile{
Result: roles{
Result: []role{
{
Name: "name1",
},
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestListProfiles(t *testing.T) {
}, nil
}

p, err := service.getProfiles("url", "token")
p, err := service.getRoles("url", "token")
if err != nil && err.Error() != test.ErrorMessage {
t.Fatalf("want %s, got %s", test.ErrorMessage, err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func GetAwsSsoToken(domain string) (string, error) {
}
}
}
return "", fmt.Errorf(fmt.Sprintf("no AWS SSO Token found, please open the AWS Management Console https://%s/start/#/ first", domain))
return "", fmt.Errorf(fmt.Sprintf("No AWS SSO token found, please finish the SSO in the user portal first: https://%s/start/#/ first", domain))
}

func decryptValue(encryptedValue []byte) string {
Expand Down
28 changes: 14 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {

func info() {
app.Name = "aws-one-punch"
app.Usage = "one punch to grant all command line windows AWS access in MacOS"
app.Usage = "one punch to grant all command prompts AWS access with IAM Role credentials in OSX."
app.Version = "1.0.0"
}

Expand Down Expand Up @@ -60,9 +60,9 @@ func commands() {
},
},
{
Name: "list-profiles",
Aliases: []string{"ls-p"},
Usage: "List profiles under an account",
Name: "list-roles",
Aliases: []string{"ls-r"},
Usage: "List IAM roles under an account",
Flags: []cli.Flag{
&cli.StringFlag{Name: "account-id", Required: true},
},
Expand All @@ -72,44 +72,44 @@ func commands() {
if err != nil {
log.Fatalln(err)
}
profiles, err := awsService.getProfiles(fmt.Sprintf("https://portal.sso.ap-southeast-2.amazonaws.com/instance/appinstance/%s/profiles", accountId), token)
roles, err := awsService.getRoles(fmt.Sprintf("https://portal.sso.ap-southeast-2.amazonaws.com/instance/appinstance/%s/profiles", accountId), token)
if err != nil {
log.Fatalln(err)
}
if len(profiles.Result) > 0 {
for i := 0; i < len(profiles.Result); i++ {
fmt.Printf("ProfileName: %s\n", profiles.Result[i].Name)
if len(roles.Result) > 0 {
for i := 0; i < len(roles.Result); i++ {
fmt.Printf("RoleName: %s\n", roles.Result[i].Name)
}
return nil
}
fmt.Printf("no profiles found for account %s\n", accountId)
fmt.Printf("no IAM roles found for account %s\n", accountId)
return nil
},
},
{
Name: "access",
Aliases: []string{"a"},
Usage: "Access AWS Resource with a profile",
Usage: "Access AWS Resource with IAM role credentials",
Flags: []cli.Flag{
&cli.StringFlag{Name: "account-name", Required: true},
&cli.StringFlag{Name: "profile-name", Required: true},
&cli.StringFlag{Name: "role-name", Required: true},
},
Action: func(c *cli.Context) error {
accountId := c.Value("account-name")
profileName := c.Value("profile-name")
roleName := c.Value("role-name")
token, err := GetAwsSsoToken(domain)
if err != nil {
log.Fatalln(err)
}
cs, err := awsService.getCredentials(fmt.Sprintf("https://portal.sso.ap-southeast-2.amazonaws.com/federation/credentials/?account_id=%s&role_name=%s&debug=true", accountId, profileName), token)
cs, err := awsService.getCredentials(fmt.Sprintf("https://portal.sso.ap-southeast-2.amazonaws.com/federation/credentials/?account_id=%s&role_name=%s&debug=true", accountId, roleName), token)
if err != nil {
log.Fatalln(err)
}
err = updateCredentialFile(cs)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("AWS access granted for account %s and profile %s\n", accountId, profileName)
fmt.Printf("AWS access granted for account %s and IAM role %s\n", accountId, roleName)
return nil
},
},
Expand Down

0 comments on commit f0d5596

Please sign in to comment.