diff --git a/docs/resources/google_logging_project_sinks.md b/docs/resources/google_logging_project_sinks.md index 44c254d2e..865a9163d 100644 --- a/docs/resources/google_logging_project_sinks.md +++ b/docs/resources/google_logging_project_sinks.md @@ -51,7 +51,7 @@ The following examples show how to use this InSpec audit resource. ### Test that a subset of all sinks matching "project*" have a particular writer identity - google_logging_project_sinks(project: 'chef-inspec-gcp').sink_names.each do |sink_name| + 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 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_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/test/integration/verify/controls/google_logging_project_sinks.rb b/test/integration/verify/controls/google_logging_project_sinks.rb index 9a2559ee4..6ad81f403 100644 --- a/test/integration/verify/controls/google_logging_project_sinks.rb +++ b/test/integration/verify/controls/google_logging_project_sinks.rb @@ -14,6 +14,6 @@ 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 eq "storage.googleapis.com/#{gcp_logging_bucket_name}" } + its('sink_destinations') { should include "storage.googleapis.com/#{gcp_logging_bucket_name}" } end end \ No newline at end of file