Class: Shelter::CLI::Command::Resource
- Inherits:
-
Thor
- Object
- Thor
- Shelter::CLI::Command::Resource
- Defined in:
- lib/cli/command/resource.rb
Overview
Resource subcommand for Shelter
Basic Directory Structure
By default Shelter is looking for a directory named resources
with a subdirectory name templates
.
In the templates
subdirectory we can define different templates. They have to be a valid CloudFormation .yaml
file.
In the resources
directory we can define different resources. They have to be defined in .yaml
format with a few specific tags in it.
+-- resources
| +-- templates
| | `-- restricted-s3.yaml
| `-- testbucketresource.yaml
You can specify where is your resources
directory in Shelterfile.rb
with resource_directory
.
Templates definition
In our example above, we have only one template named restricted-s3
. This template defined a CloudFormation stack that contains an S3 bucket, an IAM User and a Policy for that specific user which restricts the user to be able to reach only our new S3 Bucket, but nothing else. User we create the IAM User, we create a new AccessKey pair, so we can use the credentials in our infrastucrute. Now, for the simplicity we did not define a KMS key.
As an output we export the newly created AccessKeyID
and AccessKeySecret
pair.
We have three template parameters:
-
S3 Bucket name
-
Client name for tagging our S3 Bucket (reason: billing)
-
Project name for tagging our S3 Bucket (reason: billing)
resources/templates/restricted-s3.yaml:
---
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
BucketName:
Type: String
Description: Created S3 bucket
Client:
Type: String
Description: Name of the client for tagging
Project:
Type: String
Description: Project tag
Resources:
Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
Tags:
- { Key: "project", Value: !Ref Project }
- { Key: "client", Value: !Ref Client }
S3Policy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: !Join ["-", ["s3", !Ref BucketName]]
Users:
- !Ref NewUser
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "s3:PutObject"
- "s3:GetObjectAcl"
- "s3:GetObject"
- "s3:GetObjectTorrent"
- "s3:GetBucketTagging"
- "s3:GetObjectTagging"
- "s3:ListBucket"
- "s3:PutObjectTagging"
- "s3:DeleteObject"
Resource:
- !GetAtt [Bucket, Arn]
- !Join ["", [!GetAtt [Bucket, Arn], "/*"]]
NewUser:
Type: "AWS::IAM::User"
Properties:
UserName: !Join ["-", ["s3", !Ref BucketName, "user"]]
Path: /
AccessKey:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref NewUser
Outputs:
AccessKeyID:
Value:
!Ref AccessKey
SecretKeyID:
Value: !GetAtt AccessKey.SecretAccessKey
Resource definition
Now we have a template named restricted-s3
. We can start creating resources with this template. For now we can create a backup user and S3 bucket, so all of our servers with a specific label can call AWS API to make some backup on our specific S3 bucket.
Let’s create a file under resources
:
---
name: testbucketresource
template: restricted-s3
capabilities:
- CAPABILITY_NAMED_IAM
tags:
random: yes
project: test
client: cheppers
extra: something
parameters:
BucketName: my-testresource
Client: cheppers
Project: test
Here we go. Basically all of the keys in this file are required, or if we don’t define them, they will be empty (like tags).
Name
This will be the name of our resource. It will be stack name as well, but prefixed with the res-
string. In this case res-testbucketresource
.
Template
This value defines which one of our template we want to use. In this case we want to use our only one restricted-s3
which is defined in resources/templates/restricted-s3.yaml
.
Capabilities
AWS CloudFormation capabilities. For more details check. [AWS API Documentation](docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html). Now we want to manage IAM resources, so we need CAPABILITY_IAM
in general, but now we give them custom names so we need CAPABILITY_NAMED_IAM
.
Tags
This is a simple key-value list. Our CloudFormation stack will be tagged with these tags.
Parameters
This is a simple ket-value list. That’s how we can define parameters for our CloudFormation template.
Instance Method Summary collapse
-
#create(resource_name) ⇒ Object
With
create
, we can create a specific resource. -
#delete(resource_name) ⇒ Object
With
delete
we can delete the whole stack. -
#list ⇒ Object
With
list
we can check our resource inventory. -
#output(resource_name) ⇒ Object
If we defined Outputs in our template, we can easily list them all with
output
command. -
#status(resource_name) ⇒ Object
With
status
we can ask for the stack status. -
#update(resource_name) ⇒ Object
With
update
, we can update a specific resource.
Instance Method Details
#create(resource_name) ⇒ Object
With create
, we can create a specific resource.
$ bundle exec shelter resource create testbucketresource
Waiting for 'stack_create_complete' on 'res-testbucketresource'...
262 263 264 265 266 267 268 269 270 |
# File 'lib/cli/command/resource.rb', line 262 def create(resource_name) res = read_resource(resource_name) cf_client.create_stack( stack_name: res['name'], capabilities: res['capabilities'], template_body: read_template(res['template']), tags: res['tags'], parameters: res['parameters'] ) wait_until(:stack_create_complete, res['name']) end |
#delete(resource_name) ⇒ Object
With delete
we can delete the whole stack.
$ bundle exec shelter resource delete testbucketresource
Waiting for 'stack_delete_complete' on 'res-testbucketresource'...
251 252 253 254 255 |
# File 'lib/cli/command/resource.rb', line 251 def delete(resource_name) resource = read_resource(resource_name) cf_client.delete_stack(stack_name: resource['name']) wait_until(:stack_delete_complete, resource['name']) end |
#list ⇒ Object
With list
we can check our resource inventory.
$ bundle exec shelter resource list
testbucketresource
176 177 178 179 180 |
# File 'lib/cli/command/resource.rb', line 176 def list Dir.glob("#{App.config.resource_directory}/*.yaml").each do |res| puts File.basename(res, '.yaml') end end |
#output(resource_name) ⇒ Object
If we defined Outputs in our template, we can easily list them all with output
command
$ bundle exec shelter resource output testbucketresource
AccessKeyID: AKIXXXXXXXXXXXXXXXXX
SecretKeyID: 3cXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXuI
219 220 221 222 223 224 225 226 227 |
# File 'lib/cli/command/resource.rb', line 219 def output(resource_name) resource = read_resource(resource_name) stack = cf_client.describe_stacks( stack_name: resource['name'] ).stacks.first stack.outputs.each { |out| display_stack_output(out) } end |
#status(resource_name) ⇒ Object
With status
we can ask for the stack status.
$ bundle exec shelter resource status testbucketresource
Resource ID: AKIXXXXXXXXXXXXXXXXX
Resource Type: AWS::IAM::AccessKey
Resource Status: CREATE_COMPLETE
Resource ID: cheppers-testresource
Resource Type: AWS::S3::Bucket
Resource Status: CREATE_COMPLETE
Resource ID: s3-cheppers-testresource-user
Resource Type: AWS::IAM::User
Resource Status: CREATE_COMPLETE
Resource ID: res-t-S3Po-W66XXXXXXXXX
Resource Type: AWS::IAM::Policy
Resource Status: CREATE_COMPLETE
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/cli/command/resource.rb', line 198 def status(resource_name) resource = read_resource(resource_name) stack = cf_client.describe_stacks( stack_name: resource['name'] ).stacks.first cf_client.describe_stack_resources( stack_name: stack.stack_name ).stack_resources.each { |r| display_stack_resource(r) } rescue Aws::CloudFormation::Errors::ValidationError puts "#{resource_name} does not exist" end |
#update(resource_name) ⇒ Object
With update
, we can update a specific resource.
$ bundle exec shelter resource update testbucketresource
Waiting for 'stack_update_complete' on 'res-testbucketresource'...
234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/cli/command/resource.rb', line 234 def update(resource_name) res = read_resource(resource_name) cf_client.update_stack( stack_name: res['name'], capabilities: res['capabilities'], template_body: read_template(res['template']), tags: res['tags'], parameters: res['parameters'] ) wait_until(:stack_update_complete, resource['name']) rescue Aws::CloudFormation::Errors::ValidationError => e puts e. end |