Source code for examples.s3_bucket.s3_policy
#!/usr/bin/env python3
from troposphere import Output, Export, Sub, GetAtt, Join
from troposphere import s3
import boto3.s3
from tropostack.base import InlineConfStack
from tropostack.cli import InlineConfOvrdCLI
[docs]class S3BucketStack(InlineConfStack):
"""
Tropostack defining an S3 bucket with optional IP-based access restriction
Args:
allowed_cidr (str): IP CIDR range to allow access from.
Use ``0.0.0.0/0`` to allow access from anywhere.
bucket_name (str): The name of the S3 bucket to be created.
Can contain AWS variables such as ``${AWS::AccountId}``
Outputs:
BucketArn (str): The ARN of the created S3 bucket
"""
# Base name for all instances of that same stack
BASE_NAME = 'example-s3-stack'
# Since this stack is declared as InlineConf - no external configuration -
# we add any settings as part of the class itself
CONF = {
'region': 'eu-west-1',
'bucket_name': '${AWS::AccountId}-tropostack-my-test-bucket',
'allowed_cidr': '0.0.0.0/0'
}
# This is an Output element in the stack, as denoted by leading 'o_'
@property
def o_bucket_arn(self):
_id = 'BucketArn'
return Output(
_id,
Description='The ARN of the S3 bucket',
Value=GetAtt(self.r_bucket,'Arn'),
# We're exporting the output as <StackName>-<OutputName>
# Other stacks can consume the output value using the same naming
Export=Export(Sub("${AWS::StackName}-%s" % _id))
)
# Below are the Resource elements in the stack, specified by a leading 'r_'
@property
def r_bucket(self):
# Create an S3 Bucket
return s3.Bucket(
'S3Bucket',
# Bucket name comes from configuraion file
BucketName=Sub(self.conf['bucket_name']),
)
@property
def r_bucket_policy(self):
# Append a policy to the S3 bucket
return s3.BucketPolicy(
'S3BucketPolicy',
# We're addressing the bucket directly as a property of the instance
Bucket = self.r_bucket.BucketName,
PolicyDocument = {
"Statement":[{
"Action":["s3:GetObject"],
"Effect":"Allow",
# We obtain a list of permitted CIDRs from the configuration
"Resource": Join("",["arn:aws:s3:::", self.r_bucket.ref(), '/*'], ),
"Principal":"*",
"Condition":{
"IpAddress": {
"aws:SourceIp": self.conf['allowed_cidr']
}
}
}]
}
)
[docs]class AugmentedCLI(InlineConfOvrdCLI):
"""
Extend the default set of CLI commands to add a custom action.
"""
# Create a custom command, but part of the Tropostack script
[docs] def cmd_purge(self):
"""
Delete all objects inside the S3 bucket, along with the bucket itself.
"""
# Retrieve the name directly from the generated stack
bucket_name = self.stack.r_bucket.BucketName
bucket = boto3.resource('s3').Bucket(bucket_name)
try:
print('Deleting all objects in bucket: {}'.format(bucket_name))
bucket.objects.all().delete()
print('Deleting bucket itself')
bucket.delete()
except Exception as err:
print('Failed purging bucket {}: {}'.format(bucket_name, err))
raise
print('Done.')
if __name__ == '__main__':
cli = AugmentedCLI(S3BucketStack)
cli.run()