aws-asmr 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +102 -0
- data/bin/asmr +83 -0
- data/lib/aws/asmr/alias.rb +61 -0
- data/lib/aws/asmr/cache.rb +64 -0
- data/lib/aws/asmr/options.rb +52 -0
- data/lib/aws/asmr/version.rb +5 -0
- data/lib/aws/asmr.rb +34 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 295a3e23217a96e62d009c06284c2523d9397479c300c686dd2f3a750ed37a5f
|
4
|
+
data.tar.gz: 22138144c91d1cce17ebfc50c0f537954996995350483f22984ade1c380d7a91
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b4a733b089f9c05ff0e7dbeca84f3c647180e40347733385fd008b0ca23a590b7f5939a1aeaf7cab55875391e3be3ca8db3ba4e2d2618ded656acb3f8148912e
|
7
|
+
data.tar.gz: 94ece634442510edaaf7f5d2ab4e3a3dc11bc586fe60fa85c019adb9fac2247292f6cc36d8b140b19e158aea1207ad958322548119e6442157b241d98849f25f
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# Aws::ASMR
|
2
|
+
|
3
|
+
`ASMR` stands for "Assume Role", obviously!! This is a command line utility for people in the hell of aws assume_role.
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
Only gem(ruby package) install is supported now, sorry!
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install aws-asmr
|
11
|
+
gem which aws/asmr
|
12
|
+
|
13
|
+
# Set `PATH` generated from command below
|
14
|
+
gem which aws/asmr | sed -e "s/lib\/aws\/asmr.rb/bin/" | sed "s/^/PATH=/" | sed "s/$/:\$PATH/"
|
15
|
+
|
16
|
+
# Only in current shell
|
17
|
+
. <(gem which aws/asmr | sed -e "s/lib\/aws\/asmr.rb/bin/" | sed "s/^/PATH=/" | sed "s/$/:\$PATH/")
|
18
|
+
|
19
|
+
# Set PATH everytime started zsh session
|
20
|
+
gem which aws/asmr | sed -e "s/lib\/aws\/asmr.rb/bin/" | sed "s/^/PATH=/" | sed "s/$/:\$PATH/" >> ~/.zshrc
|
21
|
+
|
22
|
+
asmr --version
|
23
|
+
```
|
24
|
+
|
25
|
+
## Command Example
|
26
|
+
|
27
|
+
In the example below, you can run command `aws sts get-caller-identity` with assumed role `arn:aws:iam::0000:role/AwesomeRole` on specified aws account `custodian`.
|
28
|
+
|
29
|
+
If active *MFA* device detected on the IAM account(`custodian`), it'll prompt `MFA token code`. Please check and type the successful code and you'll see the process goes on. Once you went through the MFA, the credentials to assume role are cached on local. At the next command on the same *ARN*, you can skip MFA unless cache is expired.
|
30
|
+
|
31
|
+
Regardless that MFA is enabled or not, temporary credentials `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SECRET_TOKEN` are set in the current command when assume_role was successful, without `export` environment variables.
|
32
|
+
In the case below, you'll run a command like `AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=yyyy AWS_SECRET_TOKEN=zzzz aws sts get-caller-identity`
|
33
|
+
This means those variables are only effective for the subsequential command(`aws sts get-caller-identity`). So it is safe and you can run commands idempotently (If you export those environment variables, the same command for assume_role would never be successful in the same shell session).
|
34
|
+
|
35
|
+
```
|
36
|
+
AWS_PROFILE=custodian asmr --name=arn:aws:iam::0000:role/AwesomeRole aws sts get-caller-identity
|
37
|
+
```
|
38
|
+
|
39
|
+
Of course you can set `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` respectively to perform assume_role, instead of profile.
|
40
|
+
|
41
|
+
```
|
42
|
+
AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=yyyy asmr --name=arn:aws:iam::0000:role/AwesomeRole aws sts get-caller-identity
|
43
|
+
```
|
44
|
+
|
45
|
+
To specify ARN (or alias name of assumed role), you MUST set `name` option with a form like `--name=<arn>` NOT a form like `--name <arn>`. For short version, `-n<arn>` works, `-n <arn>` doesn't. You must be wasting time for this pitfall, sorry!
|
46
|
+
This is due to a development circumstance. This tool is supposed to run 2 commands. One is assume_role, and the other is subsequential(this is main though) command. To safely separate options for assume_role and subsequential commands, all components of the `asmr` args must be start with `-`. Curse my programming ability!
|
47
|
+
|
48
|
+
```
|
49
|
+
asmr --name=arn:aws:iam::0000:role/AwesomeRole
|
50
|
+
asmr -narn:aws:iam::0000:role/AwesomeRole
|
51
|
+
```
|
52
|
+
|
53
|
+
Of course you can set options for subsequential command.
|
54
|
+
|
55
|
+
```
|
56
|
+
asmr --name=arn:aws:iam::0000:role/AwesomeRole aws ec2 describe-instances --filter '[{"Name":"instance-state-name","Values":["stopped"]}]'
|
57
|
+
```
|
58
|
+
|
59
|
+
Unfortunatelly you need quote and appropriate escape to run piped command as subsequential.
|
60
|
+
|
61
|
+
```
|
62
|
+
asmr --name=arn:aws:iam::0000:role/AwesomeRole "aws sts get-caller-identity | grep Arn"
|
63
|
+
```
|
64
|
+
|
65
|
+
Without subsequential command, it just prints environment variables for assume_role.
|
66
|
+
|
67
|
+
```
|
68
|
+
AWS_PROFILE=custodian asmr --name=arn:aws:iam::0000:role/AwesomeRole
|
69
|
+
# AWS_ACCESS_KEY_ID=xxxx
|
70
|
+
# AWS_SECRET_ACCESS_KEY=yyyy
|
71
|
+
# AWS_SECRET_TOKEN=zzzz
|
72
|
+
```
|
73
|
+
|
74
|
+
You can define aliases as you like at `~/.aws-asmr/alias` (default).
|
75
|
+
Here is the example of alias file. `arn` is the only required attribute.
|
76
|
+
|
77
|
+
```
|
78
|
+
[awesome-app-staging]
|
79
|
+
arn = arn:aws:iam::0001:role/AwesomeRole
|
80
|
+
profile = custodian
|
81
|
+
|
82
|
+
[awesome-app-production]
|
83
|
+
arn = arn:aws:iam::0002:role/AwesomeRole
|
84
|
+
access_key_id = xxxx
|
85
|
+
secret_access_key = yyyy
|
86
|
+
|
87
|
+
# [commented-awesome-app-test]
|
88
|
+
# arn = test
|
89
|
+
```
|
90
|
+
|
91
|
+
Then, you can choose one of the alias.
|
92
|
+
|
93
|
+
```
|
94
|
+
asmr aws sts get-caller-identity
|
95
|
+
# Choose alias listed on the shell
|
96
|
+
```
|
97
|
+
|
98
|
+
Or you can specify alias name.
|
99
|
+
|
100
|
+
```
|
101
|
+
asmr --name=awesome-app-staging aws sts get-caller-identity
|
102
|
+
```
|
data/bin/asmr
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "tty-prompt"
|
4
|
+
require "aws/asmr"
|
5
|
+
require "aws/asmr/options"
|
6
|
+
|
7
|
+
asmr_args, command_args = Aws::ASMR::Options.partition(ARGV)
|
8
|
+
options = Aws::ASMR::Options.parse(asmr_args)
|
9
|
+
|
10
|
+
if options[:version]
|
11
|
+
require "aws/asmr/version"
|
12
|
+
puts Aws::ASMR::VERSION
|
13
|
+
exit(0)
|
14
|
+
elsif options[:clear]
|
15
|
+
Aws::ASMR::Cache.destroy!
|
16
|
+
exit(0)
|
17
|
+
else
|
18
|
+
end
|
19
|
+
|
20
|
+
prompt = TTY::Prompt.new
|
21
|
+
|
22
|
+
name = if options[:name]
|
23
|
+
options[:name]
|
24
|
+
else
|
25
|
+
alias_keys = Aws::ASMR::Alias.base.keys
|
26
|
+
if alias_keys.empty?
|
27
|
+
STDERR.puts "Please specify --name=ARN to assume_role or make alias at #{Aws::ASMR::ROOT}/alias"
|
28
|
+
exit(1)
|
29
|
+
end
|
30
|
+
prompt.select("Choose a role you're going to assume:", alias_keys)
|
31
|
+
end
|
32
|
+
asmr_alias = Aws::ASMR::Alias.get(name)
|
33
|
+
assume_role_arn = if asmr_alias
|
34
|
+
asmr_alias.set_environment_variables!
|
35
|
+
asmr_alias.arn
|
36
|
+
else
|
37
|
+
name
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(command, shell_variables=[])
|
41
|
+
if command.empty?
|
42
|
+
exec("echo \"#{shell_variables.join($/)}\"")
|
43
|
+
else
|
44
|
+
command = if command.length == 1
|
45
|
+
# Ex: asmr "aws sts get-caller-identity | grep Arn"
|
46
|
+
command
|
47
|
+
else
|
48
|
+
# Ex: asmr aws ec2 describe-instances --filter '[{"Name":"instance-state-name","Values":["stopped"]}]'
|
49
|
+
command.map{|plain|
|
50
|
+
if plain.match?(/[\"\'\ ]/)
|
51
|
+
quoted = plain.gsub("'"){"\\'"}
|
52
|
+
"'#{quoted}'"
|
53
|
+
else
|
54
|
+
plain
|
55
|
+
end
|
56
|
+
}
|
57
|
+
end
|
58
|
+
exec([*shell_variables, *command].join(' '))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if cache = Aws::ASMR::Cache.get(assume_role_arn)
|
63
|
+
run(command_args, cache.shell_variables)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
begin
|
68
|
+
serial_number = Aws::ASMR.detect_mfa_device_serial_number
|
69
|
+
assume_role_args = if serial_number
|
70
|
+
token_code = prompt.ask("Type MFA token code:")
|
71
|
+
{serial_number: serial_number, token_code: token_code}
|
72
|
+
else
|
73
|
+
{}
|
74
|
+
end
|
75
|
+
|
76
|
+
res = Aws::ASMR.assume_role(assume_role_arn, **assume_role_args)
|
77
|
+
cache = Aws::ASMR::Cache.new(**res.credentials.to_h)
|
78
|
+
cache.save!(assume_role_arn)
|
79
|
+
run(command_args, cache.shell_variables)
|
80
|
+
rescue => e
|
81
|
+
STDERR.puts e.message
|
82
|
+
exit(1)
|
83
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'aws/asmr'
|
3
|
+
|
4
|
+
module Aws
|
5
|
+
module ASMR
|
6
|
+
class Alias < Struct.new(:arn, :access_key_id, :secret_access_key, :profile, keyword_init: true)
|
7
|
+
|
8
|
+
PATH = "#{Aws::ASMR::ROOT}/alias"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def parse(lines)
|
12
|
+
lines = lines.map(&:strip).select{! _1.start_with?('#')}
|
13
|
+
arr = lines.reduce([]) do |acc,line|
|
14
|
+
m = line.match(/\A\[([^\[\]]+)\]\z/)
|
15
|
+
if m
|
16
|
+
acc << [m[1], []]
|
17
|
+
else
|
18
|
+
alias_name, alias_props = acc.last
|
19
|
+
if alias_name
|
20
|
+
k, v = line.split('=').map(&:strip)
|
21
|
+
if k and v
|
22
|
+
alias_props << [k, v]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
acc
|
27
|
+
end
|
28
|
+
arr.map{|k,v| [k,v.to_h]}.select{|k,v| v["arn"] and !v["arn"].empty?}.to_h
|
29
|
+
end
|
30
|
+
|
31
|
+
def base
|
32
|
+
@base ||= begin
|
33
|
+
if File.exists?(PATH)
|
34
|
+
parse(File.readlines(PATH))
|
35
|
+
else
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def first
|
42
|
+
k,v = base.first
|
43
|
+
get(k)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(alias_name)
|
47
|
+
base[alias_name] && new(base[alias_name])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_environment_variables!
|
52
|
+
if access_key_id and !access_key_id.empty?
|
53
|
+
ENV["AWS_ACCESS_KEY_ID"] = access_key_id
|
54
|
+
ENV["AWS_SECRET_ACCESS_KEY"] = secret_access_key
|
55
|
+
elsif profile and !profile.empty?
|
56
|
+
ENV["AWS_PROFILE"] = profile
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'aws/asmr'
|
3
|
+
|
4
|
+
module Aws
|
5
|
+
module ASMR
|
6
|
+
class Cache < Struct.new(:access_key_id, :secret_access_key, :session_token, :expiration, keyword_init: true)
|
7
|
+
|
8
|
+
PATH = "#{Aws::ASMR::ROOT}/cache"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def base
|
12
|
+
@base ||= begin
|
13
|
+
if File.exists?(PATH)
|
14
|
+
JSON.parse(File.read(PATH))
|
15
|
+
else
|
16
|
+
{}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def first
|
22
|
+
k,v = base.first
|
23
|
+
get(k)
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(assume_role_arn)
|
27
|
+
cache = base[assume_role_arn] && new(base[assume_role_arn])
|
28
|
+
return nil unless cache
|
29
|
+
cache.expired? ? nil : cache
|
30
|
+
end
|
31
|
+
|
32
|
+
def destroy!
|
33
|
+
File.exists?(PATH) && File.delete(PATH)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def expiration_time
|
38
|
+
return nil unless expiration
|
39
|
+
expiration.is_a?(Time) ? expiration : Time.parse(expiration)
|
40
|
+
end
|
41
|
+
|
42
|
+
def expired?
|
43
|
+
return true unless expiration_time
|
44
|
+
expiration_time < (Time.now+60*5)
|
45
|
+
end
|
46
|
+
|
47
|
+
def save!(assume_role_arn)
|
48
|
+
FileUtils.mkdir_p(Pathname.new(PATH).dirname)
|
49
|
+
self.class.base[assume_role_arn] = to_h
|
50
|
+
File.open(PATH, 'w') do |f|
|
51
|
+
f.puts(JSON.pretty_generate(self.class.base))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def shell_variables
|
56
|
+
{
|
57
|
+
AWS_ACCESS_KEY_ID: access_key_id,
|
58
|
+
AWS_SECRET_ACCESS_KEY: secret_access_key,
|
59
|
+
AWS_SESSION_TOKEN: session_token,
|
60
|
+
}.map{|k,v| "#{k}=#{v}"}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "aws/asmr"
|
3
|
+
|
4
|
+
module Aws::ASMR
|
5
|
+
module Options
|
6
|
+
def partition(args)
|
7
|
+
_idx, asmr_args, command_args = args.reduce([1, [], []]) do |acc,i|
|
8
|
+
idx, _, _ = acc
|
9
|
+
unless i.start_with?('-')
|
10
|
+
idx = 2
|
11
|
+
acc[0] = idx
|
12
|
+
end
|
13
|
+
acc[idx] << i
|
14
|
+
acc
|
15
|
+
end
|
16
|
+
[asmr_args, command_args]
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(args)
|
20
|
+
options = {}
|
21
|
+
OptionParser.new do |opts|
|
22
|
+
# opts.banner = "Usage: asmr [options]"
|
23
|
+
opts.banner = <<~EOS
|
24
|
+
You can use ALIAS to shortcut name input by setting it at #{Aws::ASMR::ROOT}/alias
|
25
|
+
|
26
|
+
Usage: asmr [options] [command] [arg...]
|
27
|
+
EOS
|
28
|
+
|
29
|
+
opts.on("-nNAME", "--name=NAME", "Name to perform assume role with ARN or ALIAS") do |name|
|
30
|
+
options[:name] = name
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-h", "--help", "Prints this help") do
|
34
|
+
puts opts
|
35
|
+
exit(0)
|
36
|
+
end
|
37
|
+
opts.on("--version", "Prints version") do
|
38
|
+
options[:version] = true
|
39
|
+
end
|
40
|
+
opts.on("--clear", "Clear cache") do
|
41
|
+
options[:clear] = true
|
42
|
+
# require "aws/asmr/version"
|
43
|
+
# puts Aws::ASMR::VERSION
|
44
|
+
# exit(0)
|
45
|
+
end
|
46
|
+
end.parse(args)
|
47
|
+
options
|
48
|
+
end
|
49
|
+
|
50
|
+
module_function :partition, :parse
|
51
|
+
end
|
52
|
+
end
|
data/lib/aws/asmr.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'aws-sdk-iam'
|
2
|
+
require 'aws-sdk-sts'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Aws
|
6
|
+
module ASMR
|
7
|
+
ROOT = if ENV["AWS_ASMR_ROOT"] && !ENV["AWS_ASMR_ROOT"].empty?
|
8
|
+
Pathname.new(ENV["AWS_ASMR_ROOT"]).join('').to_s
|
9
|
+
else
|
10
|
+
Pathname.new(ENV['HOME']).join('.aws-asmr').to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
# assume_role('arn', serial_number: 'serial-number', token_code: '012345')
|
14
|
+
def assume_role(assume_role_arn, **args)
|
15
|
+
sts = Aws::STS::Client.new(region: 'us-east-1')
|
16
|
+
res = sts.assume_role(
|
17
|
+
role_arn: assume_role_arn,
|
18
|
+
role_session_name: "aws-asmr",
|
19
|
+
**args
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def detect_mfa_device_serial_number
|
24
|
+
iam_client = Aws::IAM::Client.new(region: 'us-east-1')
|
25
|
+
res = iam_client.list_mfa_devices
|
26
|
+
res.mfa_devices.detect(&:serial_number)&.serial_number
|
27
|
+
end
|
28
|
+
|
29
|
+
module_function :assume_role, :detect_mfa_device_serial_number
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'aws/asmr/cache'
|
34
|
+
require 'aws/asmr/alias'
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aws-asmr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- metheglin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: tty-prompt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk-sts
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: aws-sdk-iam
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
description: ''
|
56
|
+
email: pigmybank@gmail.com
|
57
|
+
executables:
|
58
|
+
- asmr
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- README.md
|
63
|
+
- bin/asmr
|
64
|
+
- lib/aws/asmr.rb
|
65
|
+
- lib/aws/asmr/alias.rb
|
66
|
+
- lib/aws/asmr/cache.rb
|
67
|
+
- lib/aws/asmr/options.rb
|
68
|
+
- lib/aws/asmr/version.rb
|
69
|
+
homepage: https://rubygems.org/gems/aws-asmr
|
70
|
+
licenses:
|
71
|
+
- MIT
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.7'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.1.4
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: aws command line tool for ASMR. ASMR stands for AssumeRole, obviously!
|
92
|
+
test_files: []
|