diff --git a/docs/resources/google_logging_project_sinks.md b/docs/resources/google_logging_project_sinks.md
new file mode 100644
index 000000000..865a9163d
--- /dev/null
+++ b/docs/resources/google_logging_project_sinks.md
@@ -0,0 +1,77 @@
+---
+title: About the google_logging_project_sinks Resource
+platform: gcp
+---
+
+# google\_logging\_project\_sinks
+
+Use the `google_logging_project_sinks` InSpec audit resource to test properties of all, or a filtered group of, GCP compute project logging sinks for a project.
+
+
+
+## Syntax
+
+A `google_logging_project_sinks` resource block collects GCP project logging sinks by project then tests that group.
+
+ describe google_logging_project_sinks(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+
+Use this InSpec resource to enumerate IDs then test in-depth using `google_logging_project_sink`.
+
+ google_logging_project_sinks(project: 'chef-inspec-gcp').sink_names.each do |sink_name|
+ describe google_logging_project_sink(project: 'chef-inspec-gcp', sink: sink_name) do
+ it { should exist }
+ 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 sinks available for the project
+
+ describe google_logging_project_sinks(project: 'chef-inspec-gcp') do
+ its('count') { should be <= 100}
+ end
+
+### Test that an expected sink name is available for the project
+
+ describe google_logging_project_sinks(project: 'chef-inspec-gcp') do
+ its('sink_names') { should include "my-sink" }
+ end
+
+### Test that an expected sink destination is available for the project
+
+ describe google_logging_project_sinks(project: 'chef-inspec-gcp') do
+ its('sink_destinations') { should include "storage.googleapis.com/a-logging-bucket" }
+ end
+
+### Test that a subset of all sinks matching "project*" have a particular writer identity
+
+ google_logging_project_sinks(project: 'chef-inspec-gcp').where(sink_name: /project/).sink_names.each do |sink_name|
+ describe google_logging_project_sink(project: 'chef-inspec-gcp', sink: sink_name) do
+ its('writer_identity') { should eq "serviceAccount:my-logging-service-account.iam.gserviceaccount.com" }
+ end
+ end
+
+
+
+## Filter Criteria
+
+This resource supports the following filter criteria: `sink_name`; `sink_filter` and `sink_destination`. Any of these may be used with `where`, as a block or as a method.
+
+## Properties
+
+* `sink_names` - an array of google_logging_project_sink name strings
+* `sink_destinations`- an array of google_logging_project_sink destinations
+* `sink_filters`- an array of google_logging_project_sink filters
+
+
+
+
+## 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_logging_audit_config.md b/docs/resources/google_project_logging_audit_config.md
new file mode 100644
index 000000000..8287d7510
--- /dev/null
+++ b/docs/resources/google_project_logging_audit_config.md
@@ -0,0 +1,51 @@
+---
+title: About the google_project_logging_audit_config Resource
+platform: gcp
+---
+
+# google\_project\_logging\_audit\_config
+
+Use the `google_compute_zone` InSpec audit resource to test properties of a single GCP compute zone.
+
+
+
+## Syntax
+
+A `google_project_logging_audit_config` resource block declares the tests for a single GCP zone by project and name.
+
+ describe google_project_logging_audit_config(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+
+
+
+## Examples
+
+The following examples show how to use this InSpec audit resource.
+
+
+### Test that a GCP project logging audit configuration has a default type defined
+
+ describe google_project_logging_audit_config(project: 'chef-inspec-gcp') do
+ its('default_types') { should include 'ADMIN_READ' }
+ end
+
+
+### Test that a GCP project logging audit configuration has default exempted members
+
+ describe google_compute_zone(project: 'chef-inspec-gcp', zone: 'us-east1-b') do
+ it { should_not have_default_exempted_members }
+ end
+
+
+
+## Properties
+
+* `default_types`, `default_exempted_members`
+
+
+
+
+## GCP Permissions
+
+Ensure the [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com/) is enabled for the project.
\ No newline at end of file
diff --git a/docs/resources/google_project_metric.md b/docs/resources/google_project_metric.md
new file mode 100644
index 000000000..bc070c96a
--- /dev/null
+++ b/docs/resources/google_project_metric.md
@@ -0,0 +1,49 @@
+---
+title: About the google_project_metric Resource
+platform: gcp
+---
+
+# google\_project\_metric
+
+Use the `google_project_metric` InSpec audit resource to test properties of a single GCP project metric.
+
+
+
+## Syntax
+
+A `google_project_metric` resource block declares the tests for a single GCP zone by project and name.
+
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: 'metric_name') do
+ it { should exist }
+ end
+
+
+
+## Examples
+
+The following examples show how to use this InSpec audit resource.
+
+### Test that a GCP project metric exists
+
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: 'metric_name') do
+ it { should exist }
+ end
+
+### Test that a GCP compute zone has an expected CPU platform
+
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: 'metric_name') do
+ its('filter') { should eq "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\")" }
+ end
+
+
+
+## Properties
+
+* `filter`, `name`, `metric_descriptor`
+
+
+
+
+## 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_metrics.md b/docs/resources/google_project_metrics.md
new file mode 100644
index 000000000..b948f8ea1
--- /dev/null
+++ b/docs/resources/google_project_metrics.md
@@ -0,0 +1,70 @@
+---
+title: About the google_project_metrics Resource
+platform: gcp
+---
+
+# google\_project\_metrics
+
+Use the `google_project_metrics` InSpec audit resource to test properties of all, or a filtered group of, GCP project metrics.
+
+
+
+## Syntax
+
+A `google_project_metrics` resource block collects GCP project logging sinks by project then tests that group.
+
+ describe google_project_metrics(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+
+Use this InSpec resource to enumerate IDs then test in-depth using `google_project_metric`.
+
+ google_project_metrics(project: 'chef-inspec-gcp').sink_names.each do |metric_name|
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: metric_name) do
+ it { should exist }
+ 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 metrics available for the project
+
+ describe google_project_metrics(project: 'chef-inspec-gcp') do
+ its('count') { should be <= 100}
+ end
+
+### Test that an expected metric name is available for the project
+
+ describe google_project_metrics(project: 'chef-inspec-gcp') do
+ its('metric_names') { should include "metric-name" }
+ end
+
+### Test that a subset of all metrics with name matching "*project*" have a particular writer identity
+
+ google_project_metrics(project: 'chef-inspec-gcp').where(metric_name: /project/).metric_names.each do |metric_name|
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: metric_name) do
+ its('filter') { should eq "(protoPayload.serviceName=\"cloudresourcemanager.googleapis.com\")" }
+ end
+ end
+
+
+
+## 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.
+
+## Properties
+
+* `metric_names` - an array of google_project_metric name strings
+* `metric_filters`- an array of google_project_metric filters
+
+
+
+
+## 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/libraries/google_logging_project_sinks.rb b/libraries/google_logging_project_sinks.rb
new file mode 100644
index 000000000..6be98aa60
--- /dev/null
+++ b/libraries/google_logging_project_sinks.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+
+module Inspec::Resources
+ class GoogleLoggingProjectSinks < GcpResourceBase
+ name 'google_logging_project_sinks'
+ desc 'Verifies settings for GCP project logging sinks in bulk'
+
+ example "
+ describe google_logging_project_sinks(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(:sink_names, field: :sink_name)
+ filter_table_config.add(:sink_destinations, field: :sink_destination)
+ filter_table_config.connect(self, :fetch_data)
+
+ def fetch_data
+ sink_rows = []
+ next_page = nil
+ loop do
+ catch_gcp_errors do
+ @sinks = @gcp.gcp_client(Google::Apis::LoggingV2::LoggingService).list_project_sinks("projects/#{@project}", page_token: next_page)
+ end
+ return [] if !@sinks || !@sinks.sinks
+ @sinks.sinks.map do |sink|
+ logging_sink = @gcp.gcp_client(Google::Apis::LoggingV2::LoggingService).get_project_sink("projects/#{@project}/sinks/#{sink.name}")
+ sink_rows+=[{ sink_name: sink.name,
+ sink_destination: sink.destination,
+ sink_filter: logging_sink.filter }]
+ end
+ next_page = @sinks.next_page_token
+ break unless next_page
+ end
+ @table = sink_rows
+ end
+ end
+end
diff --git a/libraries/google_project_logging_audit_config.rb b/libraries/google_project_logging_audit_config.rb
new file mode 100644
index 000000000..224fbc9e1
--- /dev/null
+++ b/libraries/google_project_logging_audit_config.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+
+module Inspec::Resources
+ class GoogleProjectLoggingAuditConfig < GcpResourceBase
+ name 'google_project_logging_audit_config'
+ desc 'Verifies settings for a GCP project logging audit configuration'
+
+ example "
+ describe google_project_logging_audit_config(project: 'chef-inspec-gcp') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @project = opts[:project]
+ catch_gcp_errors do
+ @audit_logging_configs = @gcp.gcp_project_client.get_project_iam_policy(@project)
+ @default_types = []
+ @default_exempted_members = {}
+ if defined?(@audit_logging_configs.audit_configs)
+ @audit_logging_configs.audit_configs.each do |service_config|
+ next if service_config.service != 'allServices'
+ service_config.audit_log_configs.each do |config|
+ @default_types+=[config.log_type]
+ @default_exempted_members[config.log_type]=config.exempted_members if defined?(config.exempted_members)
+ end
+ end
+ end
+ end
+ end
+
+ def exists?
+ return false if !defined? @audit_logging_configs.audit_configs
+ !@audit_logging_configs.audit_configs.nil?
+ end
+
+ attr_reader :default_types
+
+ attr_reader :default_exempted_members
+
+ def has_default_exempted_members?
+ @default_exempted_members.values.any?
+ end
+
+ def to_s
+ "Logging Audit Config For #{@project}"
+ end
+ end
+end
diff --git a/libraries/google_project_metric.rb b/libraries/google_project_metric.rb
new file mode 100644
index 000000000..22d70a197
--- /dev/null
+++ b/libraries/google_project_metric.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+
+module Inspec::Resources
+ class GoogleProjectMetric < GcpResourceBase
+ name 'google_project_metric'
+ desc 'Verifies settings for a project metric'
+
+ example "
+ describe google_project_metric(project: 'chef-inspec-gcp', metric: 'metric_name') do
+ it { should exist }
+ end
+ "
+
+ def initialize(opts = {})
+ # Call the parent class constructor
+ super(opts)
+ @display_name = opts[:metric]
+ catch_gcp_errors do
+ @metric = @gcp.gcp_client(Google::Apis::LoggingV2::LoggingService).get_project_metric("projects/#{opts[:project]}/metrics/#{opts[:metric]}")
+ create_resource_methods(@metric)
+ end
+ end
+
+ def exists?
+ !@metric.nil?
+ end
+
+ def to_s
+ "Project Metric #{@display_name}"
+ end
+ end
+end
diff --git a/libraries/google_project_metrics.rb b/libraries/google_project_metrics.rb
new file mode 100644
index 000000000..0ce2c5220
--- /dev/null
+++ b/libraries/google_project_metrics.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'gcp_backend'
+
+module Inspec::Resources
+ class GoogleProjectMetrics < GcpResourceBase
+ name 'google_project_metrics'
+ desc 'Verifies settings for GCP project metrics in bulk'
+
+ example "
+ describe google_project_metrics(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(:metric_names, field: :metric_name)
+ filter_table_config.add(:metric_destinations, field: :metric_destination)
+ filter_table_config.connect(self, :fetch_data)
+
+ def fetch_data
+ metric_rows = []
+ next_page = nil
+ loop do
+ catch_gcp_errors do
+ @metrics = @gcp.gcp_client(Google::Apis::LoggingV2::LoggingService).list_project_metrics("projects/#{@project}", page_token: next_page)
+ end
+ return [] if !@metrics || !@metrics.metrics
+ @metrics.metrics.map do |metric|
+ metric_rows+=[{ metric_name: metric.name,
+ metric_filter: metric.filter }]
+ end
+ next_page = @metrics.next_page_token
+ break unless next_page
+ end
+ @table = metric_rows
+ end
+ end
+end
diff --git a/libraries/google_storage_bucket.rb b/libraries/google_storage_bucket.rb
index df4007632..36caec04e 100644
--- a/libraries/google_storage_bucket.rb
+++ b/libraries/google_storage_bucket.rb
@@ -29,6 +29,11 @@ def exists?
!@bucket.nil?
end
+ def has_versioning_enabled?
+ return false if !defined?(@bucket.versioning)
+ @bucket.versioning.enabled
+ end
+
def to_s
"Bucket #{@display_name}"
end
diff --git a/test/integration/verify/controls/google_logging_project_sinks.rb b/test/integration/verify/controls/google_logging_project_sinks.rb
new file mode 100644
index 000000000..6ad81f403
--- /dev/null
+++ b/test/integration/verify/controls/google_logging_project_sinks.rb
@@ -0,0 +1,19 @@
+title 'Test GCP project logging sinks'
+
+gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.')
+gcp_logging_project_sink_name = attribute(:gcp_logging_project_sink_name, default: '', description: 'The GCP project logging sink name.')
+gcp_logging_bucket_name = attribute(:gcp_logging_bucket_name, default: '', description: 'The GCP project logging bucket name.')
+gcp_enable_privileged_resources = attribute(:gcp_enable_privileged_resources,default:0,description:'Flag to enable privileged resources requiring elevated privileges in GCP.')
+
+control 'gcp-project-logging-sinks-1.0' do
+
+ only_if { gcp_enable_privileged_resources.to_i == 1 }
+ impact 1.0
+ title 'Ensure GCP project logging sinks have the correct properties in bulk.'
+
+ describe google_logging_project_sinks(project: gcp_project_id) do
+ it { should exist }
+ its('sink_names') { should include gcp_logging_project_sink_name }
+ its('sink_destinations') { should include "storage.googleapis.com/#{gcp_logging_bucket_name}" }
+ end
+end
\ No newline at end of file
diff --git a/test/integration/verify/controls/google_project_logging_audit_config.rb b/test/integration/verify/controls/google_project_logging_audit_config.rb
new file mode 100644
index 000000000..c31d18423
--- /dev/null
+++ b/test/integration/verify/controls/google_project_logging_audit_config.rb
@@ -0,0 +1,14 @@
+title 'Test GCP project logging audit configuration'
+
+gcp_project_id = attribute(:gcp_project_id, default: '', description: 'The GCP project identifier.')
+
+control 'gcp-project-audit-logging-config-1.0' do
+
+ impact 1.0
+ title 'Ensure GCP project logging audit configuration has the correct properties.'
+
+ describe google_project_logging_audit_config(project: gcp_project_id) do
+ its('default_types') { should_not match /notthere/ }
+ its('default_exempted_members') { should_not match /notthere/ }
+ end
+end
\ No newline at end of file