Created
March 24, 2020 10:58
-
-
Save janko/a8dc28b294b9b06b2902de843c665e5c to your computer and use it in GitHub Desktop.
The CLI I wrote for creating new AWS S3 buckets for reproducing issues reported for Shrine. It's can serve as an example for programmatically creating new S3 buckets with bucket-specific credentials.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
require "dry/cli" | |
require "aws-sdk-s3" | |
require "aws-sdk-iam" | |
require "json" | |
ENV["AWS_ACCESS_KEY_ID"] = "..." | |
ENV["AWS_SECRET_ACCESS_KEY"] = "..." | |
ENV["AWS_REGION"] = "..." | |
if Warning.respond_to?(:[]=) # Ruby 2.7+ | |
Warning[:deprecated] = false # TODO: remove when dry-cli fixes kwargs warnings | |
end | |
module Commands | |
extend Dry::CLI::Registry | |
class S3 | |
DEFAULT_NAME = "shrine-testing" | |
class Command < Dry::CLI::Command | |
private | |
def s3 | |
Aws::S3::Client.new | |
end | |
def iam | |
Aws::IAM::Client.new | |
end | |
end | |
class Create < Command | |
DEFAULT_PERMISSIONS = %w[ | |
GetObject | |
PutObject | |
PutObjectAcl | |
DeleteObject | |
ListMultipartUploadParts | |
AbortMultipartUpload | |
] | |
argument :name, default: DEFAULT_NAME, desc: "The name of the bucket/user" | |
option :permissions, type: :array, default: DEFAULT_PERMISSIONS, desc: "list of S3 permissions" | |
def call(name:, permissions:, **) | |
create_bucket(name) | |
create_user(name, permissions) | |
end | |
private | |
def create_bucket(name) | |
create_s3_bucket(name) | |
create_s3_bucket_cors(name) | |
end | |
def create_user(name, permissions) | |
create_iam_user(name) | |
create_iam_policy(name, permissions) | |
create_iam_access_key(name) | |
end | |
def create_s3_bucket(name) | |
s3.create_bucket(bucket: name) | |
end | |
def create_s3_bucket_cors(name) | |
s3.put_bucket_cors( | |
bucket: name, | |
cors_configuration: { | |
cors_rules: [ | |
{ | |
allowed_origins: %w[*], | |
allowed_methods: %w[GET POST PUT], | |
allowed_headers: %w[Authorization x-amz-date x-amz-content-sha256 content-type], | |
expose_headers: %w[ETag], | |
max_age_seconds: 3000, | |
}, | |
{ | |
allowed_origins: %w[*], | |
allowed_methods: %w[GET], | |
max_age_seconds: 3000, | |
}, | |
] | |
} | |
) | |
end | |
def create_iam_user(name) | |
iam.create_user(user_name: name) | |
rescue Aws::IAM::Errors::EntityAlreadyExists | |
warn "User #{name.inspect} already exists" | |
end | |
def create_iam_policy(name, permissions) | |
iam.put_user_policy( | |
user_name: name, | |
policy_name: name, | |
policy_document: JSON.generate( | |
"Version" => "2012-10-17", | |
"Statement" => [ | |
{ | |
"Effect" => "Allow", | |
"Action" => ["s3:ListBucket"], # for listing objects in a bucket | |
"Resource" => ["arn:aws:s3:::#{name}/*"], | |
}, | |
{ | |
"Effect" => "Allow", | |
"Action" => permissions.map { |name| "s3:#{name}" }, | |
"Resource" => ["arn:aws:s3:::#{name}/*"], | |
}, | |
] | |
) | |
) | |
end | |
def create_iam_access_key(name) | |
# delete any previous access keys | |
response = iam.list_access_keys(user_name: name) | |
response.access_key_metadata.each do |access_key| | |
iam.delete_access_key(user_name: name, access_key_id: access_key.access_key_id) | |
end | |
response = iam.create_access_key(user_name: name) | |
puts "access_key_id: #{response.access_key.access_key_id}" | |
puts "secret_access_key: #{response.access_key.secret_access_key}" | |
end | |
end | |
class Delete < Command | |
argument :name, default: DEFAULT_NAME, desc: "The name of the bucket/user" | |
def call(name:, **) | |
delete_user(name) | |
delete_bucket(name) | |
end | |
private | |
def delete_user(name) | |
delete_iam_policy(name) | |
delete_iam_access_keys(name) | |
delete_iam_user(name) | |
end | |
def delete_bucket(name) | |
delete_s3_bucket(name) | |
end | |
def delete_iam_policy(name) | |
iam.delete_user_policy(user_name: name, policy_name: name) | |
rescue Aws::IAM::Errors::NoSuchEntity | |
end | |
def delete_iam_access_keys(name) | |
response = iam.list_access_keys(user_name: name) | |
response.access_key_metadata.each do |access_key| | |
iam.delete_access_key(user_name: name, access_key_id: access_key.access_key_id) | |
end | |
rescue Aws::IAM::Errors::NoSuchEntity | |
end | |
def delete_iam_user(name) | |
iam.delete_user(user_name: name) | |
rescue Aws::IAM::Errors::NoSuchEntity | |
end | |
def delete_s3_bucket(name) | |
s3.delete_bucket(bucket: name) | |
rescue Aws::S3::Errors::NoSuchBucket | |
end | |
end | |
end | |
end | |
Commands.register "s3 create", Commands::S3::Create | |
Commands.register "s3 delete", Commands::S3::Delete | |
Dry::CLI.new(Commands).call |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment