diff --git a/docs/resources/aws_hosted_zones.md b/docs/resources/aws_hosted_zones.md index 8d7245f4b..ad924e3dc 100644 --- a/docs/resources/aws_hosted_zones.md +++ b/docs/resources/aws_hosted_zones.md @@ -3,58 +3,77 @@ title: About the aws_hosted_zones Resource platform: aws --- -# aws\_hosted\_zones +# aws_hosted_zones -Use the `aws_hosted_zones` resource to test the hosted zones configuration. +Use the `aws_hosted_zones` InSpec audit resource to test the properties of multiple AWS Route53 hosted zones. + +The `AWS::Route53::HostedZone` creates a new public or private hosted zone. ## Syntax -```` + +Ensure the hosted zones are available + describe aws_hosted_zones do - its('names') { should include ("carry-on.films.com") } + it { should exist } end -```` + #### Parameters This resource does not expect any parameters. +For additional information, see the [AWS documentation on the `AWS::Route53::HostedZone` resource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-hostedzone.html). ## Properties -|Property | Description| -| --- | --- | -|name | The name of the hosted zone. | -|id | It's id. | +| Property | Description | Fields | +| --- | --- | --- | +| ids | The ID that Amazon Route 53 assigned to the hosted zone when you created it. | id | +| names | The name of the domain. | name | +| caller_references | The value that you specified for CallerReference when you created the hosted zone. | caller_reference | +| configs | A complex type that includes the Comment and PrivateZone elements. | config | +| resource_record_set_counts | The number of resource record sets in the hosted zone. | resource_record_set_count | +| linked_services | If the hosted zone was created by another service, the service that created the hosted zone. | linked_service | ## Examples +### Ensure that there are more than one hosted zone. + + describe aws_hosted_zones do + its('count') { should >= 1 } + end + +### Ensure a hosted zone is available. -##### Ensure a specific hosted zone exists -```` describe aws_hosted_zones do - its('names') { should include ("carry-on.films.com") } + its('ids') { should include 'HOSTED_ZONE_ID' } + end + +### Ensure a hosted zone name is available. + + describe aws_hosted_zones do + its('names') { should include 'HOSTED_ZONE_NAME' } end -```` ## Matchers -This InSpec audit resource uses the following special matcher. For a full list of available matchers, please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/). +This InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [Universal Matchers page](https://www.inspec.io/docs/reference/matchers/). -#### should +The controls will pass if the `list` method returns at least one result. -The control will pass if the describe passes all tests. +### exist -Use `should` to validate if a specific hosted zone exists +Use `should` to test that the entity exists. -```` describe aws_hosted_zones do - its('names') { should include ("carry-on.films.com") } + it { should exist } end - -```` -## AWS Permissions +Use `should_not` to test the entity does not exist. -Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `Route53:Client:ListHostedZonesResponse` action with Effect set to Allow. + describe aws_hosted_zones do + it { should_not exist } + end -You can find detailed documentation at [Amazon Route 53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/r53-api-permissions-ref.html) +## AWS Permissions +Your [Principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/intro-structure.html#intro-structure-principal) will need the `Route53:Client:ListHostedZonesResponse` action with `Effect` set to `Allow`. diff --git a/libraries/aws_hosted_zones.rb b/libraries/aws_hosted_zones.rb index db6d80e36..3f3b097c2 100644 --- a/libraries/aws_hosted_zones.rb +++ b/libraries/aws_hosted_zones.rb @@ -2,38 +2,46 @@ require 'aws_backend' -class AwsHostedZones < AwsResourceBase +class AWSHostedZones < AwsResourceBase name 'aws_hosted_zones' - desc 'Verifies hosted zones settings are correct.' + desc 'Retrieves a list of the public and private hosted zones that are associated with the current Amazon Web Services account.' example " - describe aws_hosted_zones() do + describe aws_hosted_zones do it { should exist } - its ('name') { should include ('carry-on.films.com'))} end " - attr_reader :table - FilterTable.create - .register_column(:name, field: :name) - .register_column(:id, field: :id) - .install_filter_methods_on_resource(self, :table) + attr_reader :table def initialize(opts = {}) super(opts) validate_parameters - zones = [] - catch_aws_errors do - resp =@aws.route53_client.list_hosted_zones + @table = fetch_data + end - resp.hosted_zones.each do |zone| - zones += [{ - name: zone.name, - id: zone.id, - }] - end - end + FilterTable.create + .register_column(:ids, field: :id) + .register_column(:names, field: :name) + .register_column(:caller_references, field: :caller_reference) + .register_column(:configs, field: :config) + .register_column(:resource_record_set_counts, field: :resource_record_set_count) + .register_column(:linked_services, field: :linked_service) + .install_filter_methods_on_resource(self, :table) - @table = zones + def fetch_data + catch_aws_errors do + @resp = @aws.route53_client.list_hosted_zones.map do |table| + table.hosted_zones.map { |table_name| { + id: table_name.id, + name: table_name.name, + caller_reference: table_name.caller_reference, + config: table_name.config, + resource_record_set_count: table_name.resource_record_set_count, + linked_service: table_name.linked_service, + } + } + end.flatten + end end end diff --git a/test/integration/build/outputs.tf b/test/integration/build/outputs.tf index 2692a0ac0..f0f378d8b 100644 --- a/test/integration/build/outputs.tf +++ b/test/integration/build/outputs.tf @@ -54,7 +54,6 @@ output "aws_ebs_volume_type" { value = aws_ebs_volume.inspec_encrypted_ebs_volume.0.type } - output "aws_ebs_snapshot_id" { value = aws_ebs_snapshot.inspec_ebs_snapshot.0.id } @@ -1533,4 +1532,12 @@ output "aws_ssm_resource_data_sync_name" { output "aws_ssm_resource_data_sync_id" { value = aws_ssm_resource_data_sync.aws_ssm_resource_data_sync_test1.id +} + +output "aws_route53_zone_id" { + value = aws_route53_zone.test_zone.id +} + +output "aws_route53_zone_name" { + value = aws_route53_zone.test_zone.name } \ No newline at end of file diff --git a/test/integration/verify/controls/aws_hosted_zones.rb b/test/integration/verify/controls/aws_hosted_zones.rb index 08a9fcf4f..a6ac31106 100644 --- a/test/integration/verify/controls/aws_hosted_zones.rb +++ b/test/integration/verify/controls/aws_hosted_zones.rb @@ -1,12 +1,22 @@ -hosted_zone_name = attribute(:aws_route_53_zone) +aws_route53_zone_id = attribute(aws_route53_zone_id, value: '', description: '') +aws_route53_zone_name = attribute(aws_route53_zone_name, value: '', description: '') -control "Hosted zones tests" do - impact 1.0 - title "Hosted zones" - desc "Check hosted zones work" +title 'Ensure the hosted zone have the correct properties.' - describe aws_hosted_zones() do +control 'aws-hosted-zones-1.0' do + impact 1.0 + + describe aws_hosted_zones do it { should exist } - its ('name') { should include ("#{hosted_zone_name}.")} + its('count') { should >= 1 } + end + + describe aws_hosted_zones do + its('ids') { should include aws_route53_zone_id } + its('names') { should include aws_route53_zone_name } + its('caller_references') { should_not be_empty } + its('configs') { should_not be_empty } + its('resource_record_set_counts') { should_not include 1 } + its('linked_services') { should_not be_empty } end end \ No newline at end of file diff --git a/test/unit/resources/aws_hosted_zones_test.rb b/test/unit/resources/aws_hosted_zones_test.rb index 2a3dc61ec..c4a715867 100644 --- a/test/unit/resources/aws_hosted_zones_test.rb +++ b/test/unit/resources/aws_hosted_zones_test.rb @@ -1,36 +1,39 @@ -require 'aws-sdk-core' +require 'helper' require 'aws_hosted_zones' +require 'aws-sdk-core' -class AwsHostedZonesTests < Minitest::Test +class AWSHostedZonesConstructorTest < Minitest::Test - def test_fail_constructor_values - assert_raises(ArgumentError) { AwsHostedZones.new('not needed') } + def test_rejects_unrecognized_params + assert_raises(ArgumentError) { AWSHostedZones.new(unexpected: 9) } end - +end + +class AWSHostedZonesSuccessPathTest < Minitest::Test + def setup - zones_call = {} - zones_call[:method] = :list_hosted_zones - zones_call[:data] = { hosted_zones: - [{name: "carry-on.films.com", id: "some_random_id", caller_reference: "reference"}], - marker: "nil", is_truncated: false, next_marker: "nil", max_items: 100} - zones_call[:client] = Aws::Route53::Client - - @zones = AwsHostedZones.new(client_args: { stub_responses: true }, stub_data: [zones_call]) + mock_data = {} + mock_data[:method] = :list_hosted_zones + mock_data[:data] = { hosted_zones: + [{name: "test1", id: "test1", caller_reference: "test1"}], + marker: "nil", is_truncated: false, next_marker: "nil", max_items: 100} + mock_data[:client] = Aws::Route53::Client + @resp = AWSHostedZones.new(client_args: { stub_responses: true }, stub_data: [mock_data]) end - def test_exists_works - assert @zones.exist? + def test_hosted_zones_exists + assert @resp.exist? end - def test_includes_finds - assert @zones.name.include?("carry-on.films.com") + def ids + assert_equal(@resp.ids, ['test1']) end - def test_count_correct - assert_equal(@zones.name.count, 1) + def names + assert_equal(@resp.names, ['test1']) end - def test_not_included - refute @zones.name.include?("fast.films.com") + def caller_references + assert_equal(@resp.caller_references, ['test1']) end -end \ No newline at end of file +end