diff --git a/docs/resources/google_project_alert_policies.md b/docs/resources/google_project_alert_policies.md
new file mode 100644
index 000000000..b8e67f685
--- /dev/null
+++ b/docs/resources/google_project_alert_policies.md
@@ -0,0 +1,78 @@
+---
+title: About the google_project_alert_policies Resource
+platform: gcp
+---
+
+# google\_compute\_alert\_policies
+
+Use the `google_project_alert_policies` InSpec audit resource to test properties of all, or a filtered group of, GCP project alert policies.
+
+
+
+## Syntax
+
+A `google_project_alert_policies` resource block collects GCP project alert policies by project then tests that group.
+
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+
+Use this InSpec resource to enumerate IDs then test in-depth using `google_project_alert_policy`.
+
+ google_project_alert_policies(project: 'chef-inspec-gcp').policy_names.each do |policy_name|
+ describe google_project_alert_policy(name: policy_name) do
+ it { should exist }
+ it { should be_enabled }
+ 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 project alert policies available for the project
+
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ its('count') { should be <= 100}
+ end
+
+### Test that an expected policy name is available for the project
+
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ its('policy_names') { should include 'projects/spaterson-project/alertPolicies/9271751234503117449' }
+ end
+
+### Test whether any expected policy display name is available for the project
+
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ its('policy_display_names') { should_not include 'banned policy' }
+ end
+
+### Ensure no existing policies are inactive
+
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ its('policy_enabled_states') { should_not include false }
+ end
+
+
+
+
+## Filter Criteria
+
+This resource supports the following filter criteria: `policy_name`; `policy_display_name`; `policy_filter_list` and `policy_enabled_state`. Any of these may be used with `where`, as a block or as a method.
+
+## Properties
+
+* `policy_names` - an array of google_project_alert_policy name strings
+* `policy_display_names` - an array of google_project_alert_policy display name strings
+* `policy_enabled_states`- an array of google_project_alert_policy enabled status booleans
+* `policy_filter_lists`- an array of google_project_alert_policy_condition filter string arrays
+
+
+
+
+## GCP Permissions
+
+Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project.
\ No newline at end of file
diff --git a/docs/resources/google_project_alert_policy.md b/docs/resources/google_project_alert_policy.md
new file mode 100644
index 000000000..80f9a5f92
--- /dev/null
+++ b/docs/resources/google_project_alert_policy.md
@@ -0,0 +1,49 @@
+---
+title: About the google_project_alert_policy Resource
+platform: gcp
+---
+
+# google\_project\_alert\_policy
+
+Use the `google_project_alert_policy` InSpec audit resource to test properties of a single GCP project alert policy.
+
+
+
+## Syntax
+
+A `google_project_alert_policy` resource block declares the tests for a single GCP project alert policy by name.
+
+ describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do
+ it { should exist }
+ end
+
+
+
+## Examples
+
+The following examples show how to use this InSpec audit resource.
+
+### Test that a GCP alert policy is enabled
+
+ describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do
+ it { should be_enabled }
+ end
+
+### Test that a GCP compute alert policy display name is correct
+
+ describe google_project_alert_policy(name: 'projects/spaterson-project/alertPolicies/9271751234503117449') do
+ its('display_name') { should eq 'policy name' }
+ end
+
+
+
+## Properties
+
+* `combiner`, `conditions`, `creation_record`, `display_name`, `enabled`, `mutation_record`, `name`
+
+
+
+
+## GCP Permissions
+
+Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project.
diff --git a/docs/resources/google_project_alert_policy_condition.md b/docs/resources/google_project_alert_policy_condition.md
new file mode 100644
index 000000000..afc6905b7
--- /dev/null
+++ b/docs/resources/google_project_alert_policy_condition.md
@@ -0,0 +1,50 @@
+---
+title: About the google_project_alert_policy_condition Resource
+platform: gcp
+---
+
+# google\_project\_alert\_policy\_condition
+
+Use the `google_project_alert_policy_condition` InSpec audit resource to test properties of a single GCP project alert policy condition.
+
+
+
+## Syntax
+
+A `google_project_alert_policy_condition` resource block declares the tests for a single GCP project alert policy condition by name and filter.
+
+ describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do
+ it { should exist }
+ end
+
+
+
+## Examples
+
+The following examples show how to use this InSpec audit resource.
+
+
+### Test that a GCP project alert policy condition has a particular threshold value
+
+ describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do
+ its('condition_threshold_value'){ should eq 0.001 }
+ end
+
+### Test that a GCP project alert policy condition has a particular aggregation alignment period
+
+ describe google_project_alert_policy_condition(name: 'projects/spaterson-project/alertPolicies/9271751234503117449', filter 'project=\"spaterson-project\"') do
+ its('aggregation_alignment_period'){ should eq '60s' }
+ end
+
+
+
+## Properties
+
+* `condition_threshold_value`, `aggregation_alignment_period`, `aggregation_per_series_aligner`, `aggregation_cross_series_reducer`
+
+
+
+
+## GCP Permissions
+
+Ensure the [Stackdriver Logging API](https://console.cloud.google.com/apis/api/logging.googleapis.com/) is enabled for the project.
diff --git a/docs/resources/google_project_metrics.md b/docs/resources/google_project_metrics.md
index b948f8ea1..0cf9391a0 100644
--- a/docs/resources/google_project_metrics.md
+++ b/docs/resources/google_project_metrics.md
@@ -55,12 +55,13 @@ The following examples show how to use this InSpec audit resource.
## Filter Criteria
-This resource supports the following filter criteria: `metric_name` and `metric_filter`. Either of these may be used with `where`, as a block or as a method.
+This resource supports the following filter criteria: `metric_name`; `metric_type` and `metric_filter`. Either of these may be used with `where`, as a block or as a method.
## Properties
* `metric_names` - an array of google_project_metric name strings
-* `metric_filters`- an array of google_project_metric filters
+* `metric_filters`- an array of google_project_metric filter strings
+* `metric_types` - an array of google_project_metric type strings
diff --git a/docs/resources/google_user.md b/docs/resources/google_user.md
new file mode 100644
index 000000000..a03c7f231
--- /dev/null
+++ b/docs/resources/google_user.md
@@ -0,0 +1,61 @@
+---
+title: About the google_user Resource
+platform: gcp
+---
+
+# google\_user
+
+Use the `google_user` InSpec audit resource to test properties of a single GCP user.
+
+
+
+## Syntax
+
+A `google_user` resource block declares the tests for a single GCP user by principal email address or immutable ID.
+
+ describe google_user(user_key: 'principal_email_address@domain.com') do
+ it { should exist }
+ end
+
+
+
+## Examples
+
+The following examples show how to use this InSpec audit resource.
+
+### Test that a GCP user with specified ID exists
+
+ describe google_user(user_key: '110491234567894702010') do
+ it { should exist }
+ end
+
+### Test that a GCP user has expected full name
+
+ describe google_user(user_key: '110491234567894702010') do
+ its('name.full_name') { should eq "Bill S. Preston Esq." }
+ end
+
+### Test that a GCP user has MFA enabled
+
+ describe google_user(user_key: 'theodore_ted_logan@excellentadventure.com') do
+ it { should have_mfa_enabled }
+ end
+
+### Test that a GCP user is suspended or not
+
+ describe google_user(user_key: 'theodore_ted_logan@excellentadventure.com') do
+ it { should_not be_suspended }
+ end
+
+
+
+## Properties
+
+* `agreed_to_terms`, `archived`, `change_password_at_next_login`, `creation_time`, `customer_id`, `emails`, `etag`, `id`, `include_in_global_address_list`, `ip_whitelisted`, `is_admin`, `is_delegated_admin`, `is_enforced_in2_sv`, `is_enrolled_in2_sv`, `is_mailbox_setup`, `kind`, `last_login_time`, `name`, `non_editable_aliases`, `org_unit_path`, `primary_email`, `suspended`
+
+
+
+
+## GCP Permissions
+
+Ensure the G Suite Admin SDK [Directory API](https://developers.google.com/admin-sdk/directory/) is enabled and you have sufficient privileges to list users.
\ No newline at end of file
diff --git a/docs/resources/google_users.md b/docs/resources/google_users.md
new file mode 100644
index 000000000..35dd50392
--- /dev/null
+++ b/docs/resources/google_users.md
@@ -0,0 +1,78 @@
+---
+title: About the google_users Resource
+platform: gcp
+---
+
+# google\_users
+
+Use the `google_users` InSpec audit resource to test properties of all, or a filtered group of, GCP users.
+
+
+
+## Syntax
+
+A `google_users` resource block collects GCP users for the specified customer. As documented [here](https://developers.google.com/admin-sdk/directory/v1/reference/users/list), this defaults to the `my_customer` alias to represent your account's `customerId`.
+
+ describe google_users(customer: 'my_customer') do
+ it { should exist }
+ end
+
+The `domain` argument can optionally be provided to get fields from only one domain. Either the customer or the domain parameter must be provided.
+
+ describe google_users(domain: 'my_domain.com') do
+ it { should exist }
+ end
+
+Use this InSpec resource to enumerate IDs then test in-depth using `google_user`.
+
+ google_users(customer: 'my_customer').user_ids.each do |user_id|
+ describe google_user(user_key: user_id) do
+ it { should exist }
+ it { should_not be_suspended }
+ 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 users available for the project
+
+ describe google_users(customer: 'my_customer') do
+ its('count') { should be <= 100}
+ end
+
+### Test that an expected user is available for the project
+
+ describe google_users(customer: 'my_customer') do
+ its('user_names') { should include "Monsieur Happy" }
+ end
+
+### Test that a subset of all users with name matching "Batman" exists
+
+ google_users(customer: 'my_customer').where(user_full_name: /Batman/).user_ids.each do |user_id|
+ describe google_user(user_key: user_id) do
+ it { should exist }
+ end
+ end
+
+
+
+## Filter Criteria
+
+This resource supports the following filter criteria: `user_id`; `user_full_name` and `user_email`. Any of these may be used with `where`, as a block or as a method.
+
+## Properties
+
+* `user_ids` - an array of google_user identifier integers
+* `user_full_names` - an array of google_user full name strings
+* `user_emails`- an array of google_user primary email address strings
+
+
+
+
+## GCP Permissions
+
+Ensure the G Suite Admin SDK [Directory API](https://developers.google.com/admin-sdk/directory/) is enabled and you have sufficient privileges to list users.
\ No newline at end of file
diff --git a/libraries/google_compute_instance.rb b/libraries/google_compute_instance.rb
index 1e2d379bf..b2dd7f59d 100644
--- a/libraries/google_compute_instance.rb
+++ b/libraries/google_compute_instance.rb
@@ -145,6 +145,7 @@ def block_project_ssh_keys
return false if !defined?(@instance.metadata.items)
@instance.metadata.items.each do |element|
return true if element.key=='block-project-ssh-keys' and element.value.casecmp('true').zero?
+ return true if element.key=='block-project-ssh-keys' and element.value=='1'
end
false
end
diff --git a/libraries/google_compute_network.rb b/libraries/google_compute_network.rb
index c0d544ecc..0c5e3720d 100644
--- a/libraries/google_compute_network.rb
+++ b/libraries/google_compute_network.rb
@@ -31,6 +31,8 @@ def legacy?
return false if @network.auto_create_subnetworks
return false if !defined?(@network.gateway_i_pv4)
return false if !defined?(@network.i_pv4_range)
+ return false if @network.i_pv4_range.nil?
+ return false if @network.gateway_i_pv4.nil?
true
end
diff --git a/libraries/google_container_cluster.rb b/libraries/google_container_cluster.rb
index 11a688535..c817f9924 100644
--- a/libraries/google_container_cluster.rb
+++ b/libraries/google_container_cluster.rb
@@ -49,7 +49,8 @@ def has_legacy_abac_disabled?
def has_master_authorized_networks_enabled?
return false if !defined?(@cluster.master_authorized_networks_config)
return false if @cluster.master_authorized_networks_config.to_h.empty?
- return true if @cluster.master_authorized_networks_config.to_h=={ 'enabled': true }
+ return false if !defined?(@cluster.master_authorized_networks_config.enabled)
+ return true if @cluster.master_authorized_networks_config.enabled == true
false
end
@@ -96,6 +97,12 @@ def has_pod_security_policy_config?
false
end
+ def private_cluster?
+ return false if !defined?(@cluster.private_cluster)
+ return true if @cluster.private_cluster==true
+ false
+ end
+
def exists?
!@cluster.nil?
end
diff --git a/libraries/google_project_alert_policies.rb b/libraries/google_project_alert_policies.rb
new file mode 100644
index 000000000..21bd3ec3a
--- /dev/null
+++ b/libraries/google_project_alert_policies.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+require 'google/apis/monitoring_v3'
+
+module Inspec::Resources
+ class GoogleProjectAlertPolicies < GcpResourceBase
+ name 'google_project_alert_policies'
+ desc 'Verifies settings for GCP project alert policies in bulk'
+
+ example "
+ describe google_project_alert_policies(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @project = opts[:project]
+ end
+
+ # FilterTable setup
+ filter_table_config = FilterTable.create
+ filter_table_config.add(:policy_names, field: :policy_name)
+ filter_table_config.add(:policy_display_names, field: :policy_display_name)
+ filter_table_config.add(:policy_enabled_states, field: :policy_enabled_state)
+ filter_table_config.add(:policy_filter_lists, field: :policy_filter_list)
+ filter_table_config.connect(self, :fetch_data)
+
+ def fetch_data
+ policy_rows = []
+ catch_gcp_errors do
+ @policies = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).list_project_alert_policies("projects/#{@project}")
+ end
+ return [] if !@policies || !@policies.alert_policies
+ @policies.alert_policies.map do |policy|
+ policy_filters = []
+ policy.conditions.each do |condition|
+ next if !defined?(condition.condition_threshold.filter)
+ policy_filters+=[condition.condition_threshold.filter]
+ end
+
+ policy_rows+=[{ policy_name: policy.name,
+ policy_display_name: policy.display_name,
+ policy_enabled_state: policy.enabled,
+ policy_filter_list: policy_filters }]
+ end
+ @table = policy_rows
+ end
+ end
+end
diff --git a/libraries/google_project_alert_policy.rb b/libraries/google_project_alert_policy.rb
new file mode 100644
index 000000000..66493a0bf
--- /dev/null
+++ b/libraries/google_project_alert_policy.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+require 'google/apis/monitoring_v3'
+
+module Inspec::Resources
+ class GoogleProjectAlertPolicy < GcpResourceBase
+ name 'google_project_alert_policy'
+ desc 'Verifies settings for a single GCP project alert policy'
+
+ example "
+ describe google_project_alert_policy(policy: 'projects/spaterson-project/alertPolicies/9271751234503117449') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @display_name = opts[:policy]
+ catch_gcp_errors do
+ @policy = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).get_project_alert_policy(opts[:policy])
+ create_resource_methods(@policy)
+ end
+ end
+
+ def enabled?
+ return false if !defined?(@policy.enabled)
+ @policy.enabled
+ end
+
+ def exists?
+ !@policy.nil?
+ end
+
+ def to_s
+ "Alert Policy #{@display_name}"
+ end
+ end
+end
diff --git a/libraries/google_project_alert_policy_condition.rb b/libraries/google_project_alert_policy_condition.rb
new file mode 100644
index 000000000..94856e62b
--- /dev/null
+++ b/libraries/google_project_alert_policy_condition.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+require 'google/apis/monitoring_v3'
+
+module Inspec::Resources
+ class GoogleProjectAlertPolicyCondition < GcpResourceBase
+ name 'google_project_alert_policy_condition'
+ desc 'Verifies settings for a single GCP project alert policy condition by policy name and filter name'
+
+ example "
+ describe google_project_alert_policy_condition(policy: 'projects/my-project/alertPolicies/9271751234503117449', filter 'project=\"my-project\"') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @filter = opts[:filter]
+ @policy = opts[:policy]
+ catch_gcp_errors do
+ @policy_result = @gcp.gcp_client(Google::Apis::MonitoringV3::MonitoringService).get_project_alert_policy(@policy)
+ @condition = condition_for_filter(@filter)
+ end
+ end
+
+ def exists?
+ !@condition.nil?
+ end
+
+ def condition_for_filter(filter)
+ return nil if !defined?(@policy_result.conditions)
+ @policy_result.conditions.each do |condition|
+ next if !defined?(condition.condition_threshold.filter)
+ return condition if condition.condition_threshold.filter == filter
+ end
+ nil
+ end
+
+ def condition_threshold_value
+ return false if !defined?(@condition.condition_threshold.threshold_value)
+ @condition.condition_threshold.threshold_value
+ end
+
+ def aggregation_alignment_period
+ return false if !defined?(@condition.condition_threshold.aggregations[0].alignment_period)
+ @condition.condition_threshold.aggregations[0].alignment_period
+ end
+
+ def aggregation_per_series_aligner
+ return false if !defined?(@condition.condition_threshold.aggregations[0].per_series_aligner)
+ @condition.condition_threshold.aggregations[0].per_series_aligner
+ end
+
+ def aggregation_cross_series_reducer
+ return false if !defined?(@condition.condition_threshold.aggregations[0].cross_series_reducer)
+ @condition.condition_threshold.aggregations[0].cross_series_reducer
+ end
+
+ def to_s
+ "Alert Policy Condition #{@policy} \"#{@filter}\""
+ end
+ end
+end
diff --git a/libraries/google_project_metric.rb b/libraries/google_project_metric.rb
index 22d70a197..af46038cd 100644
--- a/libraries/google_project_metric.rb
+++ b/libraries/google_project_metric.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'gcp_backend'
+require 'google/apis/logging_v2'
module Inspec::Resources
class GoogleProjectMetric < GcpResourceBase
diff --git a/libraries/google_project_metrics.rb b/libraries/google_project_metrics.rb
index 0ce2c5220..63690c445 100644
--- a/libraries/google_project_metrics.rb
+++ b/libraries/google_project_metrics.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'gcp_backend'
+require 'google/apis/logging_v2'
module Inspec::Resources
class GoogleProjectMetrics < GcpResourceBase
@@ -22,6 +23,7 @@ def initialize(opts = {})
# FilterTable setup
filter_table_config = FilterTable.create
filter_table_config.add(:metric_names, field: :metric_name)
+ filter_table_config.add(:metric_types, field: :metric_type)
filter_table_config.add(:metric_destinations, field: :metric_destination)
filter_table_config.connect(self, :fetch_data)
@@ -35,7 +37,8 @@ def fetch_data
return [] if !@metrics || !@metrics.metrics
@metrics.metrics.map do |metric|
metric_rows+=[{ metric_name: metric.name,
- metric_filter: metric.filter }]
+ metric_filter: metric.filter,
+ metric_type: metric.metric_descriptor.type }]
end
next_page = @metrics.next_page_token
break unless next_page
diff --git a/libraries/google_user.rb b/libraries/google_user.rb
new file mode 100644
index 000000000..ad044fd24
--- /dev/null
+++ b/libraries/google_user.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+require 'google/apis/admin_directory_v1'
+
+module Inspec::Resources
+ class GoogleUser < GcpResourceBase
+ name 'google_user'
+ desc 'Verifies settings for a GCP user'
+
+ example "
+ describe google_user(user_key: '110491234567894702010') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @display_name = opts[:user_key]
+ catch_gcp_errors do
+ @user = @gcp.gcp_admin_client.get_user(opts[:user_key])
+ create_resource_methods(@user)
+ end
+ end
+
+ def exists?
+ !@user.nil?
+ end
+
+ def suspended?
+ return false if !defined?(@user.suspended)
+ @user.suspended
+ end
+
+ def has_mfa_enabled?
+ return false if !defined?(@user.is_enrolled_in2_sv)
+ @user.is_enrolled_in2_sv
+ end
+
+ def to_s
+ "User #{@display_name}"
+ end
+ end
+end
diff --git a/libraries/google_users.rb b/libraries/google_users.rb
new file mode 100644
index 000000000..af3332101
--- /dev/null
+++ b/libraries/google_users.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+require 'google/apis/admin_directory_v1'
+
+module Inspec::Resources
+ class GoogleUsers < GcpResourceBase
+ name 'google_users'
+ desc 'Verifies settings for GCP users in bulk'
+
+ example "
+ describe google_users(customer: 'my_customer') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @customer = opts[:customer] || 'my_customer'
+ @domain = opts[:domain] || ''
+ end
+
+ # FilterTable setup
+ filter_table_config = FilterTable.create
+ filter_table_config.add(:user_ids, field: :user_id)
+ filter_table_config.add(:user_full_names, field: :user_full_name)
+ filter_table_config.add(:user_emails, field: :user_email)
+ filter_table_config.connect(self, :fetch_data)
+
+ def fetch_data
+ user_rows = []
+ next_page = nil
+ loop do
+ catch_gcp_errors do
+ @users = @gcp.gcp_admin_client.list_users(customer: @customer, domain: @domain, page_token: next_page)
+ end
+ return [] if !@users || !@users.users
+ @users.users.map do |user|
+ user_rows+=[{ user_id: user.id,
+ user_name: user.name.full_name,
+ user_email: user.primary_email }]
+ end
+ next_page = @users.next_page_token
+ break unless next_page
+ end
+ @table = user_rows
+ end
+ end
+end