From 3beb1e0b60516fe8ba0901392816a982db049ec5 Mon Sep 17 00:00:00 2001 From: Ray Layadi Date: Wed, 13 Dec 2023 16:20:03 +1100 Subject: [PATCH] feat: add s3 website alias record --- s3.cfndsl.rb | 24 ++++++++++ spec/website_alias_spec.rb | 85 +++++++++++++++++++++++++++++++++++ spec/website_spec.rb | 21 +++++++++ tests/website.test.yaml | 12 ++++- tests/website_alias.test.yaml | 40 +++++++++++++++++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 spec/website_alias_spec.rb create mode 100644 tests/website_alias.test.yaml diff --git a/s3.cfndsl.rb b/s3.cfndsl.rb index cb92e8f..35d2b4e 100644 --- a/s3.cfndsl.rb +++ b/s3.cfndsl.rb @@ -74,6 +74,30 @@ website_configuration['ErrorDocument'] = website['error_document'] website_configuration['IndexDocument'] = website['index_document'] + if website.has_key?('alias') + dns_format = website['alias']['dns_format'] + subdomain = website['alias']['subdomain'] + fqdn = subdomain + "." + dns_format + "." + region = website['alias']['region'] + + # Reference: https://docs.aws.amazon.com/general/latest/gr/s3.html#s3_website_region_endpoints + s3RegionMapping = { + "ap-southeast-2" => { + "hosted_zone_id" => "Z1WCIGYICN2BYD" + } + } + + Route53_RecordSet("#{safe_bucket_name}S3AliasRecord") do + HostedZoneName FnSub("#{dns_format}.") + Name FnSub(fqdn) + Type 'A' + AliasTarget ({ + DNSName: "s3-website-#{region}.amazonaws.com.", + HostedZoneId: s3RegionMapping[region]['hosted_zone_id'] + }) + end + end + if website.has_key?('routing_rules') routing_rules = [] website['routing_rules'].each do |rule| diff --git a/spec/website_alias_spec.rb b/spec/website_alias_spec.rb new file mode 100644 index 0000000..c931836 --- /dev/null +++ b/spec/website_alias_spec.rb @@ -0,0 +1,85 @@ +require 'yaml' + +describe 'compiled component s3' do + + context 'cftest' do + it 'compiles test' do + expect(system("cfhighlander cftest #{@validate} --tests tests/website_alias.test.yaml")).to be_truthy + end + end + + let(:template) { YAML.load_file("#{File.dirname(__FILE__)}/../out/tests/website_alias/s3.compiled.yaml") } + + context "Resource" do + + + context "NormalbucketS3AliasRecord" do + let(:resource) { template["Resources"]["NormalbucketS3AliasRecord"] } + + it "is of type AWS::Route53::RecordSet" do + expect(resource["Type"]).to eq("AWS::Route53::RecordSet") + end + + it "to have property HostedZoneName" do + expect(resource["Properties"]["HostedZoneName"]).to eq({"Fn::Sub"=>"example.com."}) + end + + it "to have property Name" do + expect(resource["Properties"]["Name"]).to eq({"Fn::Sub"=>"mybucket.example.com."}) + end + + it "to have property Type" do + expect(resource["Properties"]["Type"]).to eq("A") + end + + it "to have property AliasTarget" do + expect(resource["Properties"]["AliasTarget"]).to eq({"DNSName"=>"s3-website-ap-southeast-2.amazonaws.com.", "HostedZoneId"=>"Z1WCIGYICN2BYD"}) + end + + end + + context "Normalbucket" do + let(:resource) { template["Resources"]["Normalbucket"] } + + it "is of type AWS::S3::Bucket" do + expect(resource["Type"]).to eq("AWS::S3::Bucket") + end + + it "to have property BucketName" do + expect(resource["Properties"]["BucketName"]).to eq({"Fn::Sub"=>"normal-bucket"}) + end + + it "to have property Tags" do + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-normal-bucket"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) + end + + it "to have property PublicAccessBlockConfiguration" do + expect(resource["Properties"]["PublicAccessBlockConfiguration"]).to eq({"BlockPublicAcls"=>false, "BlockPublicPolicy"=>false, "IgnorePublicAcls"=>true, "RestrictPublicBuckets"=>false}) + end + + it "to have property WebsiteConfiguration" do + expect(resource["Properties"]["WebsiteConfiguration"]).to eq({"ErrorDocument"=>"error.html", "IndexDocument"=>"index.html", "RoutingRules"=>[{"RedirectRule"=>{"HostName"=>"test1", "HttpRedirectCode"=>"301", "Protocol"=>"http", "ReplaceKeyWith"=>"test1"}, "RoutingRuleCondition"=>{"HttpErrorCodeReturnedEquals"=>"400", "KeyPrefixEquals"=>"test1"}}, {"RedirectRule"=>{"ReplaceKeyPrefixWith"=>"documents/"}, "RoutingRuleCondition"=>{"KeyPrefixEquals"=>"docs/"}}]}) + end + + end + + context "NormalbucketPolicy" do + let(:resource) { template["Resources"]["NormalbucketPolicy"] } + + it "is of type AWS::S3::BucketPolicy" do + expect(resource["Type"]).to eq("AWS::S3::BucketPolicy") + end + + it "to have property Bucket" do + expect(resource["Properties"]["Bucket"]).to eq({"Ref"=>"Normalbucket"}) + end + + it "to have property PolicyDocument" do + expect(resource["Properties"]["PolicyDocument"]).to eq({"Statement"=>[{"Sid"=>"s3-website", "Effect"=>"Allow", "Principal"=>"*", "Resource"=>[{"Fn::Join"=>["", ["arn:aws:s3:::", {"Ref"=>"Normalbucket"}]]}, {"Fn::Join"=>["", ["arn:aws:s3:::", {"Ref"=>"Normalbucket"}, "/*"]]}], "Action"=>["s3:GetObject"]}]}) + end + + end + + end + +end \ No newline at end of file diff --git a/spec/website_spec.rb b/spec/website_spec.rb index 79a4301..3626534 100644 --- a/spec/website_spec.rb +++ b/spec/website_spec.rb @@ -28,12 +28,33 @@ expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-normal-bucket"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) end + it "to have property PublicAccessBlockConfiguration" do + expect(resource["Properties"]["PublicAccessBlockConfiguration"]).to eq({"BlockPublicAcls"=>false, "BlockPublicPolicy"=>false, "IgnorePublicAcls"=>true, "RestrictPublicBuckets"=>false}) + end + it "to have property WebsiteConfiguration" do expect(resource["Properties"]["WebsiteConfiguration"]).to eq({"ErrorDocument"=>"error.html", "IndexDocument"=>"index.html", "RoutingRules"=>[{"RedirectRule"=>{"HostName"=>"test1", "HttpRedirectCode"=>"301", "Protocol"=>"http", "ReplaceKeyWith"=>"test1"}, "RoutingRuleCondition"=>{"HttpErrorCodeReturnedEquals"=>"400", "KeyPrefixEquals"=>"test1"}}, {"RedirectRule"=>{"ReplaceKeyPrefixWith"=>"documents/"}, "RoutingRuleCondition"=>{"KeyPrefixEquals"=>"docs/"}}]}) end end + context "NormalbucketPolicy" do + let(:resource) { template["Resources"]["NormalbucketPolicy"] } + + it "is of type AWS::S3::BucketPolicy" do + expect(resource["Type"]).to eq("AWS::S3::BucketPolicy") + end + + it "to have property Bucket" do + expect(resource["Properties"]["Bucket"]).to eq({"Ref"=>"Normalbucket"}) + end + + it "to have property PolicyDocument" do + expect(resource["Properties"]["PolicyDocument"]).to eq({"Statement"=>[{"Sid"=>"s3-website", "Effect"=>"Allow", "Principal"=>"*", "Resource"=>[{"Fn::Join"=>["", ["arn:aws:s3:::", {"Ref"=>"Normalbucket"}]]}, {"Fn::Join"=>["", ["arn:aws:s3:::", {"Ref"=>"Normalbucket"}, "/*"]]}], "Action"=>["s3:GetObject"]}]}) + end + + end + end end \ No newline at end of file diff --git a/tests/website.test.yaml b/tests/website.test.yaml index 80a8678..2b180a0 100644 --- a/tests/website.test.yaml +++ b/tests/website.test.yaml @@ -23,4 +23,14 @@ buckets: - redirect_rule: replace_key_prefix_with: "documents/" routing_rule_condition: - key_prefix_equals: "docs/" \ No newline at end of file + key_prefix_equals: "docs/" + public_access_block_configuration: + BlockPublicAcls: false + BlockPublicPolicy: false + IgnorePublicAcls: true + RestrictPublicBuckets: false + bucket-policy: + s3-website: + actions: + - s3:GetObject + principal: "*" \ No newline at end of file diff --git a/tests/website_alias.test.yaml b/tests/website_alias.test.yaml new file mode 100644 index 0000000..1190a65 --- /dev/null +++ b/tests/website_alias.test.yaml @@ -0,0 +1,40 @@ +test_metadata: + type: config + name: website_alias + description: set the description for your test + +# Insert your tests here +buckets: + normal-bucket: + type: default + website: + redirect_requests: false + error_document: error.html + index_document: index.html + alias: + dns_format: example.com + subdomain: mybucket + region: ap-southeast-2 + routing_rules: + - redirect_rule: + hostname: "test1" + http_redirect_code: "301" + protocol: "http" + replace_key_with: "test1" + routing_rule_condition: + http_error_code_returned_equals: "400" + key_prefix_equals: "test1" + - redirect_rule: + replace_key_prefix_with: "documents/" + routing_rule_condition: + key_prefix_equals: "docs/" + public_access_block_configuration: + BlockPublicAcls: false + BlockPublicPolicy: false + IgnorePublicAcls: true + RestrictPublicBuckets: false + bucket-policy: + s3-website: + actions: + - s3:GetObject + principal: "*" \ No newline at end of file