diff --git a/Gemfile b/Gemfile index b55115e1d..8b50cb85b 100644 --- a/Gemfile +++ b/Gemfile @@ -15,5 +15,5 @@ group :development do end group :inspec do - gem 'inspec', '~> 2.1', '>= 2.1.78' + gem 'inspec', '~> 2.2', '>= 2.2.10' end diff --git a/docs/resources/google_compute_firewalls.md.erb b/docs/resources/google_compute_firewalls.md.erb new file mode 100644 index 000000000..4666d3cd5 --- /dev/null +++ b/docs/resources/google_compute_firewalls.md.erb @@ -0,0 +1,76 @@ +--- +title: About the google_compute_firewalls Resource +platform: gcp +--- + +# google\_compute\_firewalls + +Use the `google_compute_firewalls` InSpec audit resource to test properties of all, or a filtered group of, GCP compute firewalls for a project. + +
+ +## Syntax + +A `google_compute_firewalls` resource block collects GCP firewalls by project then tests that group. + + describe google_compute_firewalls(project: 'chef-inspec-gcp') do + it { should exist } + end + +Use this InSpec resource to enumerate IDs then test in-depth using `google_compute_firewall`. + + google_compute_firewalls(project: 'chef-inspec-gcp').firewall_names.each do |firewall_name| + describe google_compute_firewall(project: 'chef-inspec-gcp', name: firewall_name) do + it { should exist } + its('kind') { should eq "compute#firewall" } + end + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that there are no more than a specified number of firewalls available for the project + + describe google_compute_firewalls(project: 'chef-inspec-gcp') do + its('count') { should be <= 100} + end + +### Test that an expected firewall is available for the project + + describe google_compute_firewalls(project: 'chef-inspec-gcp') do + its('firewall_names') { should include "my-app-firewall-rule" } + end + +### Test that a particular named rule does not exist + + describe google_compute_firewalls(project: 'chef-inspec-gcp') do + its('firewall_names') { should_not include "default-allow-ssh" } + end + +### Test there are no firewalls for the "INGRESS" direction + + describe google_compute_firewalls(project: 'chef-inspec-gcp').where(firewall_direction: 'INGRESS') do + it { should_not exist } + end + +
+ +## Filter Criteria + +This resource supports the following filter criteria: `firewall_id`; `firewall_name`; and `firewall_direction`. Any of these may be used with `where`, as a block or as a method. + +## Properties + +* `firewall_ids` - an array of google_compute_firewall identifier integers +* `firewall_names` - an array of google_compute_firewall name strings +* `firewall_directions`- an array of google_compute_firewall directions containing strings e.g. "INGRESS" or "EGRESS" + +
+ + +## GCP Permissions + +Ensure the [Compute Engine API](https://console.cloud.google.com/apis/library/compute.googleapis.com/) is enabled for the project where the resource is located. \ No newline at end of file diff --git a/docs/resources/google_compute_instance.md.erb b/docs/resources/google_compute_instance.md.erb index 479847bfa..457bdf3d4 100644 --- a/docs/resources/google_compute_instance.md.erb +++ b/docs/resources/google_compute_instance.md.erb @@ -75,11 +75,17 @@ The following examples show how to use this InSpec audit resource. its('first_network_interface_type'){ should eq "one_to_one_nat" } end +### Test that a particular compute instance label key is present + + describe google_compute_instance(project: 'chef-inspec-gcp', zone: 'us-east1-b', name: 'inspec-test-vm') do + its('labels_keys') { should include 'my_favourite_label' } + end +
## Properties -* `cpu_platform`, `creation_timestamp`, `deletion_protection`, `disks`, `id`, `kind`, `label_fingerprint`, `machine_type`, `metadata`, `name`, `network_interfaces`, `scheduling`, `start_restricted`, `status`, `tags`, `zone` +* `cpu_platform`, `creation_timestamp`, `deletion_protection`, `disks`, `id`, `kind`, `label_fingerprint`, `machine_type`, `metadata`, `name`, `network_interfaces`, `scheduling`, `start_restricted`, `status`, `tags`, `zone`, `labels_keys`, `labels_values`
diff --git a/docs/resources/google_compute_instances.md.erb b/docs/resources/google_compute_instances.md.erb index 2837f7edb..7569edbcd 100644 --- a/docs/resources/google_compute_instances.md.erb +++ b/docs/resources/google_compute_instances.md.erb @@ -5,7 +5,7 @@ platform: gcp # google\_compute\_instances -Use the `google_compute_instances` InSpec audit resource to test properties of all GCP compute instances for a project in a particular zone. +Use the `google_compute_instances` InSpec audit resource to test properties of all, or a filtered group of, GCP compute instances for a project in a particular zone.
@@ -38,7 +38,7 @@ The following examples show how to use this InSpec audit resource. ### Test that there are no more than a specified number of instances in the project and zone describe google_compute_instances(project: 'chef-inspec-gcp', zone: 'europe-west2-a') do - its('entries.count') { should be <= 100} + its('count') { should be <= 100} end ### Test the exact number of instances in the project and zone @@ -57,11 +57,12 @@ The following examples show how to use this InSpec audit resource. ## Filter Criteria -This resource currently does not support any filter criteria; it will always fetch all instances in the zone. +This resource supports the following filter criteria: `instance_id` and `instance_name`. Either of these may be used with `where`, as a block or as a method. ## Properties -* `instance_id`, `instance_name` +* `instance_ids` - an array of google_compute_instance identifier integers +* `instance_names` - an array of google_compute_instance name strings
diff --git a/docs/resources/google_compute_zone.md.erb b/docs/resources/google_compute_zone.md.erb new file mode 100644 index 000000000..6f826445c --- /dev/null +++ b/docs/resources/google_compute_zone.md.erb @@ -0,0 +1,57 @@ +--- +title: About the google_compute_zone Resource +platform: gcp +--- + +# google\_compute\_zone + +Use the `google_compute_zone` InSpec audit resource to test properties of a single GCP compute zone. + +
+ +## Syntax + +A `google_compute_zone` resource block declares the tests for a single GCP zone by project and name. + + describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do + its('name') { should match 'us-east1-b' } + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that a GCP compute zone exists + + describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do + it { should exist } + end + +### Test that a GCP compute zone is in the expected state + + describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do + its('status') { should eq 'UP' } + # or equivalently + it { should be_up } + end + +### Test that a GCP compute zone has an expected CPU platform + + describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do + its('available_cpu_platforms') { should include "Intel Skylake" } + end + +
+ +## Properties + +* `available_cpu_platforms`, `creation_timestamp`, `description`, `id`, `kind`, `name`, `region`, `status`, `region_name` + +
+ + +## GCP Permissions + +Ensure the [Compute Engine API](https://console.cloud.google.com/apis/library/compute.googleapis.com/) is enabled for the project where the resource is located. \ No newline at end of file diff --git a/docs/resources/google_compute_zones.md.erb b/docs/resources/google_compute_zones.md.erb new file mode 100644 index 000000000..a5c24b476 --- /dev/null +++ b/docs/resources/google_compute_zones.md.erb @@ -0,0 +1,77 @@ +--- +title: About the google_compute_zones Resource +platform: gcp +--- + +# google\_compute\_zones + +Use the `google_compute_zones` InSpec audit resource to test properties of all, or a filtered group of, GCP compute zones for a project in a particular zone. + +
+ +## Syntax + +A `google_compute_zones` resource block collects GCP zones by project then tests that group. + + describe google_compute_zones(project: 'chef-inspec-gcp') do + it { should exist } + end + +Use this InSpec resource to enumerate IDs then test in-depth using `google_compute_zone`. + + google_compute_zones(project: 'chef-inspec-gcp').zone_names.each do |zone_name| + describe google_compute_zone(project: 'chef-inspec-gcp', zone: zone_name) do + it { should exist } + its('kind') { should eq "compute#zone" } + its('status') { should eq 'UP' } + end + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +### Test that there are no more than a specified number of zones available for the project + + describe google_compute_zones(project: 'chef-inspec-gcp') do + its('count') { should be <= 100} + end + +### Test the exact number of zones in the project + + describe google_compute_zones(project: 'chef-inspec-gcp') do + its('zone_ids.count') { should cmp 9 } + end + +### Test that an expected zone is available for the project + + describe google_compute_zones(project: 'chef-inspec-gcp') do + its('zone_names') { should include "us-east1-b" } + end + +### Test whether any zones are in status "DOWN" + + describe google_compute_zones(project: 'chef-inspec-gcp') do + its('zone_statuses') { should_not include "DOWN" } + end + +
+ +## Filter Criteria + +This resource supports the following filter criteria: `zone_id`; `zone_name` and `zone_status`. Anyy of these may be used with `where`, as a block or as a method. + +## Properties + +* `zone_ids` - an array of google_compute_zone identifier integers +* `zone_names` - an array of google_compute_zone name strings +* `zone_statuses`- an array of google_compute_zone statuses + +
+ + +## GCP Permissions + +Ensure the [Compute Engine API](https://console.cloud.google.com/apis/library/compute.googleapis.com/) is enabled for the project where the resource is located. \ No newline at end of file diff --git a/libraries/google_compute_firewall.rb b/libraries/google_compute_firewall.rb index 8de5ee601..01e23b92c 100644 --- a/libraries/google_compute_firewall.rb +++ b/libraries/google_compute_firewall.rb @@ -8,7 +8,7 @@ class GoogleComputeFirewall < GcpResourceBase desc 'Verifies settings for a compute firewall rule' example " - describe google_compute_firewall(project: 'chef-inspec-gcp', location: 'us-west2', name: 'gcp-inspec-test') do + describe google_compute_firewall(project: 'chef-inspec-gcp', name: 'gcp-inspec-test') do it { should exist } its('name') { should eq 'inspec-test' } its('status') { should eq 'in_use' } @@ -51,6 +51,10 @@ def ports_protocol_allowed(port_list, protocol = 'tcp', index = 0) end end + def exists? + !@firewall.nil? + end + def to_s "Firewall Rule #{@display_name}" end diff --git a/libraries/google_compute_firewalls.rb b/libraries/google_compute_firewalls.rb new file mode 100644 index 000000000..e2333dea8 --- /dev/null +++ b/libraries/google_compute_firewalls.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'gcp_backend' + +module Inspec::Resources + class GoogleComputeFirewalls < GcpResourceBase + name 'google_compute_firewalls' + desc 'Verifies settings for GCP compute firewalls in bulk' + + example " + describe google_compute_firewalls(project: 'chef-inspec-gcp') do + it { should exist } + ... + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @display_name = opts[:name] + @project = opts[:project] + end + + # FilterTable setup + filter_table_config = FilterTable.create + filter_table_config.add(:firewall_ids, field: :firewall_id) + filter_table_config.add(:firewall_names, field: :firewall_name) + filter_table_config.add(:firewall_directions, field: :firewall_direction) + filter_table_config.connect(self, :fetch_data) + + def fetch_data + firewall_rows = [] + next_page = nil + loop do + catch_gcp_errors do + @firewalls = @gcp.gcp_compute_client.list_firewalls(@project, page_token: next_page) + end + return [] if !@firewalls.items + @firewalls.items.map do |firewall| + firewall_rows+=[{ firewall_id: firewall.id, + firewall_name: firewall.name, + firewall_direction: firewall.direction }] + end + next_page = @firewalls.next_page_token + break unless next_page + end + @table = firewall_rows + end + end +end diff --git a/libraries/google_compute_instance.rb b/libraries/google_compute_instance.rb index e1726577f..9e2351a4c 100644 --- a/libraries/google_compute_instance.rb +++ b/libraries/google_compute_instance.rb @@ -98,6 +98,18 @@ def machine_size machine_type.split('/').last end + # helper for returning label keys to perform checks + def labels_keys + return [] if !defined?(labels) + labels.item.keys + end + + # helper for returning label values to perform checks + def labels_values + return [] if !defined?(labels) + labels.item.values + end + def exists? !@instance.nil? end diff --git a/libraries/google_compute_instances.rb b/libraries/google_compute_instances.rb index 2b2aa8da1..875255b85 100644 --- a/libraries/google_compute_instances.rb +++ b/libraries/google_compute_instances.rb @@ -24,23 +24,24 @@ def initialize(opts = {}) # FilterTable setup filter_table_config = FilterTable.create - filter_table_config.add_accessor(:where) - filter_table_config.add_accessor(:entries) - filter_table_config.add(:exist?) { |filter_table| !filter_table.entries.empty? } - filter_table_config.add(:count) { |filter_table| filter_table.entries.count } filter_table_config.add(:instance_ids, field: :instance_id) filter_table_config.add(:instance_names, field: :instance_name) - filter_table_config.add(:colors, field: :color, type: :simple) filter_table_config.connect(self, :fetch_data) def fetch_data instance_rows = [] - catch_gcp_errors do - @instances = @gcp.gcp_compute_client.list_instances(@project, @zone) - end - @instances.items.map do |instance| - instance_rows+=[{ instance_id: instance.id, - instance_name: instance.name }] + next_page = nil + loop do + catch_gcp_errors do + @instances = @gcp.gcp_compute_client.list_instances(@project, @zone, page_token: next_page) + end + return [] if !@instances.items + @instances.items.map do |instance| + instance_rows+=[{ instance_id: instance.id, + instance_name: instance.name }] + end + next_page = @instances.next_page_token + break unless next_page end @table = instance_rows end diff --git a/libraries/google_compute_zone.rb b/libraries/google_compute_zone.rb new file mode 100644 index 000000000..678c10278 --- /dev/null +++ b/libraries/google_compute_zone.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'gcp_backend' + +module Inspec::Resources + class GoogleComputeZone < GcpResourceBase + name 'google_compute_zone' + desc 'Verifies settings for a zone' + + example " + describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do + it { should exist } + its('name') { should match 'us-east1-b' } + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @display_name = opts[:name] + catch_gcp_errors do + @zone = @gcp.gcp_compute_client.get_zone(opts[:project], opts[:name]) + create_resource_methods(@zone) + end + end + + # helper method for retrieving a region name + def region_name + return false if !defined?(region) + region.split('/').last + end + + def exists? + !@zone.nil? + end + + def up? + return false if !defined?(status) + status == 'UP' + end + + def to_s + "Zone #{@display_name}" + end + end +end diff --git a/libraries/google_compute_zones.rb b/libraries/google_compute_zones.rb new file mode 100644 index 000000000..c0a688e5d --- /dev/null +++ b/libraries/google_compute_zones.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'gcp_backend' + +module Inspec::Resources + class GoogleComputeZones < GcpResourceBase + name 'google_compute_zones' + desc 'Verifies settings for GCP compute zones in bulk' + + example " + describe google_compute_zones(project: 'chef-inspec-gcp') do + it { should exist } + ... + end + " + + def initialize(opts = {}) + # Call the parent class constructor + super(opts) + @display_name = opts[:name] + @project = opts[:project] + end + + # FilterTable setup + filter_table_config = FilterTable.create + filter_table_config.add(:zone_ids, field: :zone_id) + filter_table_config.add(:zone_names, field: :zone_name) + filter_table_config.add(:zone_statuses, field: :zone_status) + filter_table_config.connect(self, :fetch_data) + + def fetch_data + zone_rows = [] + catch_gcp_errors do + @zones = @gcp.gcp_compute_client.list_zones(@project) + end + return [] if !@zones.items + @zones.items.map do |zone| + zone_rows+=[{ zone_id: zone.id, + zone_name: zone.name, + zone_status: zone.status }] + end + @table = zone_rows + end + end +end diff --git a/test/integration/configuration/gcp_inspec_config.rb b/test/integration/configuration/gcp_inspec_config.rb index 9b943cea8..f8c391b56 100644 --- a/test/integration/configuration/gcp_inspec_config.rb +++ b/test/integration/configuration/gcp_inspec_config.rb @@ -18,6 +18,7 @@ module GCPInspecConfig # Determine the storage account name and the admin password :gcp_location => "europe-west2", :gcp_zone => "europe-west2-a", + :gcp_zone_id => "2290", :gcp_int_vm_name => "gcp-inspec-int-linux-vm", :gcp_int_vm_size => "f1-micro", :gcp_int_vm_image => "ubuntu-os-cloud/ubuntu-1604-lts", @@ -64,7 +65,11 @@ module GCPInspecConfig # be disabled meaning a user needs no special GCP privileges to run the integration test pack. # # Note, would prefer to use boolean true or false here but will revisit for a future version of tf, see here for more detail: https://www.terraform.io/docs/configuration/variables.html - :gcp_enable_privileged_resources => 0 + :gcp_enable_privileged_resources => 0, + # Some controls make use of the gcloud command and grep to discover live data to then test against. + # Only test execution is affected by this flag, resource creation via terraform is unaffected. + # Default behaviour is for this to be disabled, enable by changing the below flag. + :gcp_enable_gcloud_calls => 0 } def self.config diff --git a/test/integration/verify/controls/generic_external_vm.rb b/test/integration/verify/controls/generic_external_vm.rb index 48a58274f..45bc051e8 100644 --- a/test/integration/verify/controls/generic_external_vm.rb +++ b/test/integration/verify/controls/generic_external_vm.rb @@ -52,6 +52,6 @@ its('first_network_interface_name'){ should eq "external-nat" } its('first_network_interface_type'){ should eq "one_to_one_nat" } + its('labels_keys') { should_not include 'label_does_not_exist' } end - end diff --git a/test/integration/verify/controls/generic_external_vm_data_disk.rb b/test/integration/verify/controls/generic_external_vm_data_disk.rb index cf6cee00b..33033ddbb 100644 --- a/test/integration/verify/controls/generic_external_vm_data_disk.rb +++ b/test/integration/verify/controls/generic_external_vm_data_disk.rb @@ -53,6 +53,7 @@ its('first_network_interface_nat_ip_exists'){ should be true } its('first_network_interface_name'){ should eq "external-nat" } its('first_network_interface_type'){ should eq "one_to_one_nat" } + its('labels_keys') { should_not include 'non_existing_label' } end diff --git a/test/integration/verify/controls/google_compute_firewalls.rb b/test/integration/verify/controls/google_compute_firewalls.rb new file mode 100644 index 000000000..134e8efcc --- /dev/null +++ b/test/integration/verify/controls/google_compute_firewalls.rb @@ -0,0 +1,25 @@ +title 'Firewalls Properties' + +gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') +gcp_enable_gcloud_calls = attribute(:gcp_enable_gcloud_calls,default:0,description:'Flag to enable the use of gcloud command line to pull in live data to test against.') + +control 'gcp-firewalls-1.0' do + + only_if { gcp_enable_gcloud_calls.to_i == 1 } + impact 1.0 + title 'Ensure firewalls have the correct properties in bulk' + + describe google_compute_firewalls(project: gcp_project_id) do + it { should exist } + its('count') { should be <= 100} + # assume this is a development setup for a moment + its('firewall_names') { should include "default-allow-ssh" } + its('firewall_names') { should include "default-allow-rdp" } + its('firewall_names') { should include "default-allow-internal" } + its('firewall_names') { should include "default-allow-icmp" } + # Only make the call if the configuration flag is specified and the test will run + gcp_firewall_id = `gcloud compute firewall-rules list --filter="name=default-allow-ssh" --format=json | grep id | grep -o '[0-9]\\+'`.chomp.to_i + its('firewall_ids') { should include gcp_firewall_id } + end + +end \ No newline at end of file diff --git a/test/integration/verify/controls/google_compute_firewalls_loop.rb b/test/integration/verify/controls/google_compute_firewalls_loop.rb new file mode 100644 index 000000000..f34d0fffc --- /dev/null +++ b/test/integration/verify/controls/google_compute_firewalls_loop.rb @@ -0,0 +1,17 @@ +title 'Loop over all GCP Firewalls' + +gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') + +control 'gcp-firewalls-loop-1.0' do + + impact 1.0 + title 'Ensure firewalls have the correct properties in bulk using google_compute_firewall for detail.' + + google_compute_firewalls(project: gcp_project_id).firewall_names.each do |firewall_name| + describe google_compute_firewall(project: gcp_project_id, name: firewall_name) do + it { should exist } + its('kind') { should eq "compute#firewall" } + its('direction') { should be_in ["INGRESS","EGRESS"] } + end + end +end \ No newline at end of file diff --git a/test/integration/verify/controls/google_compute_instance_label_loop.rb b/test/integration/verify/controls/google_compute_instance_label_loop.rb new file mode 100644 index 000000000..1ccc914ab --- /dev/null +++ b/test/integration/verify/controls/google_compute_instance_label_loop.rb @@ -0,0 +1,17 @@ +title 'Loop over all GCP Zones to find all Compute Instances with a particular Label' + +gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') + +control 'gcp-zones-compute-label-loop-1.0' do + + impact 1.0 + title 'Ensure labels for compute instances across all zones have or do not have a particular label.' + + google_compute_zones(project: gcp_project_id).zone_names.each do |zone_name| + google_compute_instances(project: gcp_project_id, zone: zone_name).instance_names.each do |instance_name| + describe google_compute_instance(project: gcp_project_id, zone: zone_name, name: instance_name) do + its('labels_keys') { should_not include 'operations_override_do_not_kill' } + end + end + end +end \ No newline at end of file diff --git a/test/integration/verify/controls/google_compute_vms.rb b/test/integration/verify/controls/google_compute_vms.rb index 467a74d0c..d50342792 100644 --- a/test/integration/verify/controls/google_compute_vms.rb +++ b/test/integration/verify/controls/google_compute_vms.rb @@ -3,17 +3,22 @@ gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') gcp_zone = attribute(:gcp_zone, default: '', description: 'The GCP zone being used.') gcp_ext_vm_data_disk_name = attribute(:gcp_ext_vm_data_disk_name, default: '', description: 'A valid GCP VM name to check for.') +gcp_enable_gcloud_calls = attribute(:gcp_enable_gcloud_calls,default:0,description:'Flag to enable the use of gcloud command line to pull in live data to test against.') control 'gcp-vms-1.0' do + only_if { gcp_enable_gcloud_calls.to_i == 1 } impact 1.0 title 'Ensure VMs have the correct properties in bulk' describe google_compute_instances(project: gcp_project_id, zone: gcp_zone) do it { should exist } - its('entries.count') { should be <= 100} + its('count') { should be <= 100} its('instance_ids.count') { should cmp 9 } its('instance_names') { should include gcp_ext_vm_data_disk_name } + # Only make the call if the configuration flag is specified and the test will run + gcp_instance_id = `gcloud compute instances list --filter="name=gcp-inspec-ext-linux-vm" --format=json | grep id | grep -o '[0-9]\\+'`.chomp.to_i + its('instance_ids') { should include gcp_instance_id } end end \ No newline at end of file diff --git a/test/integration/verify/controls/google_compute_zones.rb b/test/integration/verify/controls/google_compute_zones.rb new file mode 100644 index 000000000..e1f357054 --- /dev/null +++ b/test/integration/verify/controls/google_compute_zones.rb @@ -0,0 +1,20 @@ +title 'Zones Properties' + +gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') +gcp_zone = attribute(:gcp_zone, default: '', description: 'The GCP zone being used.') +gcp_zone_id = attribute(:gcp_zone_id, default: '', description: 'A sample zone identifier to test for.') + +control 'gcp-zones-1.0' do + + impact 1.0 + title 'Ensure zones have the correct properties in bulk' + + describe google_compute_zones(project: gcp_project_id) do + it { should exist } + its('count') { should be <= 100} # 46 at the time of writing + its('zone_names') { should include gcp_zone } + its('zone_statuses') { should_not include "DOWN" } + its('zone_ids') { should include gcp_zone_id.to_i } + end + +end \ No newline at end of file diff --git a/test/integration/verify/controls/google_compute_zones_loop.rb b/test/integration/verify/controls/google_compute_zones_loop.rb new file mode 100644 index 000000000..0731b29f7 --- /dev/null +++ b/test/integration/verify/controls/google_compute_zones_loop.rb @@ -0,0 +1,19 @@ +title 'Loop over all GCP Zones' + +gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.') + +control 'gcp-zones-loop-1.0' do + + impact 1.0 + title 'Ensure zones have the correct properties in bulk using google_compute_zone for detail.' + + google_compute_zones(project: gcp_project_id).zone_names.each do |zone_name| + describe google_compute_zone(project: gcp_project_id, name: zone_name) do + it { should exist } + its('kind') { should eq "compute#zone" } + its('status') { should eq 'UP' } + # or equivalently + it { should be_up } + end + end +end \ No newline at end of file diff --git a/test/integration/verify/inspec.yml b/test/integration/verify/inspec.yml index 21eada108..d4b55b917 100644 --- a/test/integration/verify/inspec.yml +++ b/test/integration/verify/inspec.yml @@ -1,6 +1,6 @@ name: inspec-gcp-integration-tests version: 0.2.0 -inspec_version: '>= 2.1.78' +inspec_version: '>= 2.2.10' depends: - name: inspec-gcp path: ../../../