Skip to content

Commit

Permalink
Add support for .ssmshrc configuration file
Browse files Browse the repository at this point in the history
  • Loading branch information
bwhaley committed Sep 29, 2019
1 parent e09561c commit 56e4f51
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 143 deletions.
22 changes: 22 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@
[[constraint]]
branch = "master"
name = "github.com/aws/aws-sdk-go"

[[constraint]]
version = "v1.2.3"
name = "gopkg.in/gcfg.v1"
207 changes: 123 additions & 84 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,142 +17,180 @@ ssmsh is an interactive shell for the EC2 Parameter Store. Features:
1. Download [here](https://github.com/bwhaley/ssmsh/releases) or clone and build from this repo.
2. Set up [AWS credentials](http://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html#specifying-credentials).

## Configuration

You can set up a `.ssmshrc` to configure `ssmsh`. By default, `ssmsh` will load `~/.ssmshrc` if it exists. Use the `-config` argument to set a different path.

```bash
[default]
type=SecureString
overwrite=true
decrypt=true
profile=my-profile
region=us-east-1
key=3example-89a6-4880-b544-73ad3db2ff3b
```

A few notes on configuration:
* When setting the region, the `AWS_REGION` env var takes top priority, followed by the setting in `.ssmshrc`, followed by the value set in the AWS profile (if configured)
* When setting the profile, the `AWS_PROFILE` env var takes top priority, followed by the setting in `.ssmshrc`
* If you set a KMS key, it will only work in the region where that key is located. You can use the `key` command while in the shell to change the key.

## Usage
### Help
```bash
/>help
/> help

Commands:
cd change your relative location within the parameter store
clear clear the screen
cp copy source to dest
decrypt toggle parameter decryption
exit exit the program
get get parameters
help display help
history get parameter history
ls list parameters
mv move parameters
policy create named parameter policy
profile switch the active AWS credentials profile
put set parameter
region change region
rm remove parameters
cd change your relative location within the parameter store
clear clear the screen
cp copy source to dest
decrypt toggle parameter decryption
exit exit the program
get get parameters
help display help
history get parameter history
key set the KMS key
ls list parameters
mv move parameters
policy create named parameter policy
profile switch to a different AWS IAM profile
put set parameter
region change region
rm remove parameters
```

### List contents of a path
Note: Listing a large number of parameters may take a long time because the maximum number of results per API call is 10. Press ^C to interrupt if a listing is taking too long. Example usage:
```bash
/>ls /House
Lannister/
Stark/
Targaryen/
/> ls
dev/
/> ls -r
/dev/app/url
/dev/db/password
/dev/db/username
/> ls /dev/app
url
/dev>
```

### Change dir and list from current working dir
```bash
/>cd /House
/House>ls
Lannister/
Stark/
Targaryen/
/> cd /dev
/dev> ls
app/
db/
/dev>
```

### Get parameter
### Get a parameter
```bash
/>cd /House/Stark
/House/Stark>get JonSnow
/> get /dev/db/username
[{
Name: "/House/Stark/JonSnow",
Type: "String",
Value: "Bastard",
Version: 2
}]
```

### Get encrypted parameters
```bash
/>cd /House/Stark
/House/Stark>get VerySecretInformation
[{
Name: "/House/Stark/VerySecretInformation",
ARN: "arn:aws:ssm:us-east-1:012345678901:parameter/dev/db/username",
LastModifiedDate: 2019-09-29 23:22:19 +0000 UTC,
Name: "/dev/db/username",
Type: "SecureString",
Value: "AQICAHhBW4N+....",
Value: "foo",
Version: 1
}]
/House/Stark>decrypt
Decrypt is true
/House/Stark>get VerySecretInformation
/> cd /dev/db
/dev/db> get ../app/url
[{
Name: "/House/Stark/VerySecretInformation",
ARN: "arn:aws:ssm:us-east-1:318677964956:parameter/dev/app/url",
LastModifiedDate: 2019-09-29 23:22:49 +0000 UTC,
Name: "/dev/app/url",
Type: "SecureString",
Value: "The three-eyed raven lives.",
Value: "https://www.example.com",
Version: 1
}]
/dev/db>
```

### Toggle decryption for SecureString parameters
```bash
/> decrypt
Decrypt is false
/> decrypt true
Decrypt is true
/>
```

### Get parameter history
```bash
/>history /House/Stark/JonSnow
/> history /dev/app/url
[{
Description: "Bastard son of Eddard",
LastModifiedDate: 2017-11-06 23:59:02 +0000 UTC,
LastModifiedUser: "bwhaley",
Name: "/House/Stark/JonSnow",
Type: "String",
Value: "Bastard",
KeyId: "alias/aws/ssm",
Labels: [],
LastModifiedDate: 2019-09-29 23:22:49 +0000 UTC,
LastModifiedUser: "arn:aws:iam::318677964956:root",
Name: "/dev/app/url",
Policies: [],
Tier: "Standard",
Type: "SecureString",
Value: "https://www.example.com",
Version: 1
} {
Description: "Bastard son of Eddard Stark, man of the Night's Watch",
LastModifiedDate: 2017-11-06 23:59:05 +0000 UTC,
LastModifiedUser: "bwhaley",
Name: "/House/Stark/JonSnow",
Type: "String",
Value: "Bastard",
Version: 2
}]
```

### Copy a parameter
```bash
/> cp /House/Stark/SansaStark /House/Lannister/SansaStark
/> cp /dev/app/url /test/app/url
/> ls -r /dev/app /test/app
/dev/app:
/dev/app/url
/test/app:
/test/app/url
```

### Copy an entire hierarchy
```bash
/> cp -R /House/Stark /House/Targaryen
/> cp -r /dev /test
/> ls -r /test
/test/app/url
/test/db/password
/test/db/username
```

### Remove parameters
```bash
/> rm /House/Stark/EddardStark
/> cd /House/Stark
/House/Stark> rm -r ../Lannister
/> rm /test/app/url
/> ls -r /test
/test/db/password
/test/db/username
/> rm -r /test
/> ls -r /test
/>
```

### Put new parameters
```bash
Multiline:
/> put
Input options. End with a blank line.
... name=/House/Targaryen/DaenerysTargaryen
... value="Khaleesi"
... name=/dev/app/domain
... value="www.example.com"
... type=String
... description="Mother of Dragons"
... description="The domain of the app in dev"
...
/>
```
Alternatively:
Single line version:

```bash
/> put name=/House/Targaryen/Daenerys value="Khaleesi" type=String description="Mother of Dragons"
/> put name=/dev/app/domain value="www.example.com" type=String description="The domain of the app in dev"
```

### Advanced parameters with policies
Use [parameter policies](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-policies.html) to do things like expire (automatically delete) parameters at a specified time:
```bash
/> policy RobbStarkExpiration Expiration(Timestamp=2013-03-31T21:00:00.000Z)
/> policy urlExpiration Expiration(Timestamp=2013-03-31T21:00:00.000Z)
/> policy ReminderPolicy ExpirationNotification(Before=30,Unit=days) NoChangeNotification(After=7,Unit=days)
/> put name=/House/Stark/Robb value="King in the North" type=String policies=[RobbStarkExpiration,ReminderPolicy]
/> put name=/dev/app/url value="www.example.com" type=String policies=[urlExpiration,ReminderPolicy]
```

### Switch profile
### Switch AWS profile
Switches to another profile as configured in `~/.aws/config`.
```bash
/> profile
default
Expand All @@ -170,35 +208,36 @@ eu-central-1
```

### Operate on other regions

A few examples of working with regions.
```bash
/> put region=eu-central-1 name=/House/Targaryen/DaenerysTargaryen value="Khaleesi" type=String description="Mother of Dragons"
/> cp -r us-west-2:/House/Stark/ eu-central-1:/House/Targaryen
/> get eu-central-1:/House/Stark/JonSnow us-west-2:/House/Stark/JonSnow
/> put region=eu-central-1 name=/dev/app/domain value="www.example.com" type=String description="The domain of the app in dev"
/> cp -r us-east-1:/dev us-west-2:/dev
/> ls -r us-west-2:/dev
/> region us-east-2
/> get us-west-2:/dev/db/username us-east-1:/dev/db/password
```

### Read commands in batches
```bash
$ cat << EOF > commands.txt
put name=/House/Targaryen/DaenerysTargaryen value="Khaleesi" type=String description="Mother of Dragons"
rm /House/Stark/RobStark
cp -R /House/Baratheon /House/Lannister
put name=/dev/app/domain value="www.example.com" type=String description="The domain of the app in dev"
rm /dev/app/domain
cp -r /dev /test
EOF
$ ssmsh -file commands.txt
$ cat commands.txt | ssmsh -file - # Read commands from STDIN
```

### Inline commands
```
$ ssmsh put name=/House/Lannister/CerseiLannister value="Noble" description="Daughter of Tywin" type=string
$ ssmsh put name=/dev/app/domain value="www.example.com" type=String description="The domain of the app in dev"
```

## todo (maybe)
* [ ] Flexible and improved output formats
* [ ] Release via homebrew
* [ ] Copy between accounts using profiles
* [ ] Find parameter
* [ ] update parameter (put with fewer required fields)
* [ ] Integration w/ CloudWatch Events for scheduled parameter updates
* [ ] Export/import
* [ ] Support globbing and/or regex
Expand Down
20 changes: 20 additions & 0 deletions aws/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package aws

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
)

func NewSession(region, profile string) *session.Session {
return session.Must(
session.NewSessionWithOptions(
session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: aws.Config{
Region: aws.String(region),
},
Profile: profile,
},
),
)
}
1 change: 1 addition & 0 deletions commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func Init(_shell *ishell.Shell, _ps *parameterstore.ParameterStore) {
registerCommand("decrypt", "toggle parameter decryption", decrypt, decryptUsage)
registerCommand("get", "get parameters", get, getUsage)
registerCommand("history", "get parameter history", history, historyUsage)
registerCommand("key", "set the KMS key", key, keyUsage)
registerCommand("ls", "list parameters", ls, lsUsage)
registerCommand("mv", "move parameters", mv, mvUsage)
registerCommand("policy", "create named parameter policy", policy, policyUsage)
Expand Down
1 change: 1 addition & 0 deletions commands/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func cp(c *ishell.Context) {
if len(paths) != 2 {
shell.Println("Expected src and dst")
shell.Println(cpUsage)
return
}
err := ps.Copy(parsePath(paths[0]), parsePath(paths[1]), recurse)
if err != nil {
Expand Down
Loading

0 comments on commit 56e4f51

Please sign in to comment.