From af2d85caabeb206099f476a08ee2669c5b734570 Mon Sep 17 00:00:00 2001 From: hi-artem Date: Fri, 11 Nov 2022 10:38:42 -0800 Subject: [PATCH] add logging settings resource --- CHANGELOG.md | 3 + docs/index.md | 6 +- docs/resources/logging_settings.md | 79 ++++++++ .../resource.tf | 16 ++ internal/api/settings/logging.go | 46 +++++ internal/convert/logging.go | 63 +++++++ internal/provider/common.go | 1 + internal/provider/provider.go | 1 + .../provider/resource_settings_logging.go | 173 ++++++++++++++++++ .../resource_settings_logging_test.go | 109 +++++++++++ templates/index.md.tmpl | 6 +- 11 files changed, 497 insertions(+), 6 deletions(-) create mode 100644 docs/resources/logging_settings.md create mode 100644 examples/resources/prismacloudcompute_logging_settings/resource.tf create mode 100644 internal/api/settings/logging.go create mode 100644 internal/convert/logging.go create mode 100644 internal/provider/resource_settings_logging.go create mode 100644 internal/provider/resource_settings_logging_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index d9f024b..20b7073 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ # Changelog +## Version 0.8.5 - 2022-10-04 +#### Added +- Add new resource to manage logging settings ## Version 0.5.0 - 2022-02-07 #### Added diff --git a/docs/index.md b/docs/index.md index 3a248d4..707a0c1 100644 --- a/docs/index.md +++ b/docs/index.md @@ -40,19 +40,19 @@ Resources representing policies can become really long. It is often helpful to s variable "hosts" { default = [ { - name: "ec2-develop, + name: "ec2-development", compliance_check: [ { id: 16 } ] }, { - name: "ec2-staging, + name: "ec2-staging", compliance_check: [ { id: 16, block: true } ] }, { - name: "ec2-staging, + name: "ec2-production", compliance_check: [ { id: 16, block: true }, { id: 18, block: false } diff --git a/docs/resources/logging_settings.md b/docs/resources/logging_settings.md new file mode 100644 index 0000000..93dff05 --- /dev/null +++ b/docs/resources/logging_settings.md @@ -0,0 +1,79 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "prismacloudcompute_logging_settings Resource - terraform-provider-prismacloudcompute" +subcategory: "" +description: |- + +--- + +# prismacloudcompute_logging_settings (Resource) + + + +## Example Usage + +```terraform +resource "prismacloudcompute_logging_settings" "enable_all" { + include_runtime_link = true + enable_metrics_collection = true + stdout { + enabled = true + verbose_scan = true + all_proc_events = true + } + syslog { + enabled = true + verbose_scan = true + all_proc_events = true + address = "https://api.datadoghq.com" + identifier = "prisma-syslog" + } +} +``` + + +## Schema + +### Required + +- `stdout` (Block List, Min: 1, Max: 1) Configuration for the stdout logging. (see [below for nested schema](#nestedblock--stdout)) +- `syslog` (Block List, Min: 1, Max: 1) Configuration for the syslog daemons of the underlying hosts. (see [below for nested schema](#nestedblock--syslog)) + +### Optional + +- `console_address` (String) Prisma Cloud Compute console url. +- `enable_metrics_collection` (Boolean) Enable prometheus instrumentation. +- `include_runtime_link` (Boolean) Include link to runtime events. + +### Read-Only + +- `id` (String) The ID of the logging settings. + + +### Nested Schema for `stdout` + +Required: + +- `enabled` (Boolean) Enable syslog logging. + +Optional: + +- `all_proc_events` (Boolean) Detailed output of all process activity (not recommended). +- `verbose_scan` (Boolean) Detailed output for vulnerabilities and compliance. + + + +### Nested Schema for `syslog` + +Required: + +- `enabled` (Boolean) Enable syslog logging. + +Optional: + +- `address` (String) Send syslog messages to a network endpoint. +- `all_proc_events` (Boolean) Detailed output of all process activity (not recommended). +- `identifier` (String) Custom identifier to all syslog messages. +- `verbose_scan` (Boolean) Detailed output for vulnerabilities and compliance. + + diff --git a/examples/resources/prismacloudcompute_logging_settings/resource.tf b/examples/resources/prismacloudcompute_logging_settings/resource.tf new file mode 100644 index 0000000..8f9b09f --- /dev/null +++ b/examples/resources/prismacloudcompute_logging_settings/resource.tf @@ -0,0 +1,16 @@ +resource "prismacloudcompute_logging_settings" "enable_all" { + include_runtime_link = true + enable_metrics_collection = true + stdout { + enabled = true + verbose_scan = true + all_proc_events = true + } + syslog { + enabled = true + verbose_scan = true + all_proc_events = true + address = "https://api.datadoghq.com" + identifier = "prisma-syslog" + } +} diff --git a/internal/api/settings/logging.go b/internal/api/settings/logging.go new file mode 100644 index 0000000..6710157 --- /dev/null +++ b/internal/api/settings/logging.go @@ -0,0 +1,46 @@ +package settings + +import ( + "fmt" + "net/http" + + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api" +) + +const SettingsLoggingEndpoint = "api/v1/settings/logging" + +type LoggingSettings struct { + SysLog SyslogSpec `json:"syslog,omitempty"` + StdOut StdOutSpec `json:"stdout,omitempty"` + EnableMetricsCollection bool `json:"enableMetricsCollection,omitempty"` + IncludeRuntimeLink bool `json:"includeRuntimeLink,omitempty"` + ConsoleAddress string `json:"consoleAddress,omitempty"` +} + +type SyslogSpec struct { + Enabled bool `json:"enabled,omitempty"` + VerboseScan bool `json:"verboseScan,omitempty"` + AllProcEvents bool `json:"allProcEvents,omitempty"` + Address string `json:"addr,omitempty"` + ID string `json:"id,omitempty"` +} + +type StdOutSpec struct { + Enabled bool `json:"enabled,omitempty"` + VerboseScan bool `json:"verboseScan,omitempty"` + AllProcEvents bool `json:"allProcEvents,omitempty"` +} + +// Get the current logging settings. +func GetLoggingSettings(c api.Client) (LoggingSettings, error) { + var ans LoggingSettings + if err := c.Request(http.MethodGet, SettingsLoggingEndpoint, nil, nil, &ans); err != nil { + return ans, fmt.Errorf("error getting logging settings: %s", err) + } + return ans, nil +} + +// Update the current logging settings. +func UpdateLoggingSettings(c api.Client, settings LoggingSettings) error { + return c.Request(http.MethodPost, SettingsLoggingEndpoint, nil, settings, nil) +} diff --git a/internal/convert/logging.go b/internal/convert/logging.go new file mode 100644 index 0000000..4bb6768 --- /dev/null +++ b/internal/convert/logging.go @@ -0,0 +1,63 @@ +package convert + +import ( + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api/settings" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func SchemaToLoggingSettings(d *schema.ResourceData) (settings.LoggingSettings, error) { + loggingSettings := settings.LoggingSettings{} + loggingSettings.ConsoleAddress = d.Get("console_address").(string) + loggingSettings.IncludeRuntimeLink = d.Get("include_runtime_link").(bool) + loggingSettings.EnableMetricsCollection = d.Get("enable_metrics_collection").(bool) + sysLogElements := d.Get("syslog").([]interface{}) + loggingSettings.SysLog = schemaToSysLogSpec(sysLogElements[0].(map[string]interface{})) + stdOutLogElements := d.Get("stdout").([]interface{}) + loggingSettings.StdOut = schemaToStdOutSpec(stdOutLogElements[0].(map[string]interface{})) + + return loggingSettings, nil +} + +func schemaToSysLogSpec(d map[string]interface{}) settings.SyslogSpec { + logSpecToSchema := settings.SyslogSpec{} + logSpecToSchema.Address = d["address"].(string) + logSpecToSchema.AllProcEvents = d["all_proc_events"].(bool) + logSpecToSchema.Enabled = d["enabled"].(bool) + logSpecToSchema.VerboseScan = d["verbose_scan"].(bool) + logSpecToSchema.ID = d["identifier"].(string) + + return logSpecToSchema +} + +func schemaToStdOutSpec(d map[string]interface{}) settings.StdOutSpec { + logSpecToSchema := settings.StdOutSpec{} + logSpecToSchema.AllProcEvents = d["all_proc_events"].(bool) + logSpecToSchema.Enabled = d["enabled"].(bool) + logSpecToSchema.VerboseScan = d["verbose_scan"].(bool) + + return logSpecToSchema +} + +func SysLogSpecToSchema(in settings.SyslogSpec) []interface{} { + m := make(map[string]interface{}) + m["enabled"] = in.Enabled + m["verbose_scan"] = in.VerboseScan + m["all_proc_events"] = in.AllProcEvents + m["address"] = in.Address + m["identifier"] = in.ID + + s := make([]interface{}, 1) + s[0] = m + return s +} + +func StdOutSpecToSchema(in settings.StdOutSpec) []interface{} { + m := make(map[string]interface{}) + m["enabled"] = in.Enabled + m["verbose_scan"] = in.VerboseScan + m["all_proc_events"] = in.AllProcEvents + + s := make([]interface{}, 1) + s[0] = m + return s +} diff --git a/internal/provider/common.go b/internal/provider/common.go index 7645ca8..bf97af7 100644 --- a/internal/provider/common.go +++ b/internal/provider/common.go @@ -15,4 +15,5 @@ const ( policyTypeVulnerabilityHost = "hostVulnerability" policyTypeVulnerabilityImage = "containerVulnerability" feedTypeCustomMalware = "customMalware" + settingsTypeLogging = "logging" ) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index b84f075..31ae070 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -76,6 +76,7 @@ func Provider() *schema.Provider { "prismacloudcompute_credential": resourceCredentials(), "prismacloudcompute_custom_compliance": resourceCustomCompliance(), "prismacloudcompute_custom_malware": resourceCustomMalwareFeed(), + "prismacloudcompute_logging_settings": resourceLoggingSettings(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/internal/provider/resource_settings_logging.go b/internal/provider/resource_settings_logging.go new file mode 100644 index 0000000..c58f748 --- /dev/null +++ b/internal/provider/resource_settings_logging.go @@ -0,0 +1,173 @@ +package provider + +import ( + "fmt" + + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api" + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api/settings" + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/convert" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceLoggingSettings() *schema.Resource { + return &schema.Resource{ + Create: createLoggingSettings, + Read: readLoggingSettings, + Update: updateLoggingSettings, + Delete: deleteLoggingSettings, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "id": { + Description: "The ID of the logging settings.", + Type: schema.TypeString, + Computed: true, + }, + "enable_metrics_collection": { + Description: "Enable prometheus instrumentation.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "include_runtime_link": { + Description: "Include link to runtime events.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "console_address": { + Description: "Prisma Cloud Compute console url.", + Type: schema.TypeString, + Optional: true, + Default: "", + }, + "syslog": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "Configuration for the syslog daemons of the underlying hosts.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + Description: "Enable syslog logging.", + }, + "verbose_scan": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Detailed output for vulnerabilities and compliance.", + }, + "all_proc_events": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Detailed output of all process activity (not recommended).", + }, + "address": { + Type: schema.TypeString, + Optional: true, + Description: "Send syslog messages to a network endpoint.", + Default: "", + }, + "identifier": { + Type: schema.TypeString, + Optional: true, + Description: "Custom identifier to all syslog messages.", + Default: "", + }, + }, + }, + }, + "stdout": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "Configuration for the stdout logging.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + Description: "Enable syslog logging.", + }, + "verbose_scan": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Detailed output for vulnerabilities and compliance.", + }, + "all_proc_events": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Detailed output of all process activity (not recommended).", + }, + }, + }, + }, + }, + } +} + +func createLoggingSettings(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + parsedLoggingSettings, err := convert.SchemaToLoggingSettings(d) + + if err != nil { + return fmt.Errorf("error creating logging settings feed '%+v': %s", parsedLoggingSettings, err) + } + + if err := settings.UpdateLoggingSettings(*client, parsedLoggingSettings); err != nil { + return fmt.Errorf("error creating %s logging settings feed: %s", settingsTypeLogging, err) + } + + d.SetId(settingsTypeLogging) + return readLoggingSettings(d, meta) +} + +func readLoggingSettings(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + retrievedLoggingSettings, err := settings.GetLoggingSettings(*client) + if err != nil { + return fmt.Errorf("error reading %s logging settings: %s", settingsTypeLogging, err) + } + + d.Set("enable_metrics_collection", retrievedLoggingSettings.EnableMetricsCollection) + d.Set("include_runtime_link", retrievedLoggingSettings.IncludeRuntimeLink) + d.Set("console_address", retrievedLoggingSettings.ConsoleAddress) + + if err := d.Set("stdout", convert.StdOutSpecToSchema(retrievedLoggingSettings.StdOut)); err != nil { + return fmt.Errorf("error reading %s logging settings: %s", settingsTypeLogging, err) + } + + if err := d.Set("syslog", convert.SysLogSpecToSchema(retrievedLoggingSettings.SysLog)); err != nil { + return fmt.Errorf("error reading %s logging settings: %s", settingsTypeLogging, err) + } + + return nil +} + +func updateLoggingSettings(d *schema.ResourceData, meta interface{}) error { + client := meta.(*api.Client) + parsedLoggingSettings, err := convert.SchemaToLoggingSettings(d) + if err != nil { + return fmt.Errorf("error updating logging settings feed '%+v': %s", parsedLoggingSettings, err) + } + + if err := settings.UpdateLoggingSettings(*client, parsedLoggingSettings); err != nil { + return fmt.Errorf("error creating %s logging settings feed: %s", settingsTypeLogging, err) + } + + d.SetId(settingsTypeLogging) + return readLoggingSettings(d, meta) +} + +func deleteLoggingSettings(d *schema.ResourceData, meta interface{}) error { + // TODO: reset to default policy + return nil +} diff --git a/internal/provider/resource_settings_logging_test.go b/internal/provider/resource_settings_logging_test.go new file mode 100644 index 0000000..87e5322 --- /dev/null +++ b/internal/provider/resource_settings_logging_test.go @@ -0,0 +1,109 @@ +package provider + +import ( + "reflect" + "testing" + + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api/settings" + "github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/convert" +) + +type stdOutSpec struct { + enabled bool + verbose_scan bool + all_proc_events bool +} + +type sysLogSpec struct { + enabled bool + verbose_scan bool + all_proc_events bool + address string + identifier string +} + +func TestFlattenStdOut(t *testing.T) { + cases := []struct { + stdOut settings.StdOutSpec + expected stdOutSpec + }{ + { + stdOut: settings.StdOutSpec{ + Enabled: true, + VerboseScan: true, + AllProcEvents: true, + }, + expected: stdOutSpec{ + enabled: true, + verbose_scan: true, + all_proc_events: true, + }, + }, + } + + for _, c := range cases { + out := convert.StdOutSpecToSchema(c.stdOut) + stdOut := out[0].(map[string]interface{}) + + if !reflect.DeepEqual(stdOut["enabled"], c.expected.enabled) { + t.Fatalf("Error matching output and expected for stdout enabled: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(stdOut["verbose_scan"], c.expected.verbose_scan) { + t.Fatalf("Error matching output and expected for stdout verbose_scan: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(stdOut["all_proc_events"], c.expected.all_proc_events) { + t.Fatalf("Error matching output and expected for stdout all_proc_events: %#v vs %#v", out, c.expected) + } + } +} + +func TestFlattenSysLog(t *testing.T) { + cases := []struct { + sysLog settings.SyslogSpec + expected sysLogSpec + }{ + { + sysLog: settings.SyslogSpec{ + Enabled: true, + VerboseScan: true, + AllProcEvents: true, + ID: "testing", + Address: "http://example.com", + }, + expected: sysLogSpec{ + enabled: true, + verbose_scan: true, + all_proc_events: true, + identifier: "testing", + address: "http://example.com", + }, + }, + } + + for _, c := range cases { + out := convert.SysLogSpecToSchema(c.sysLog) + sysLog := out[0].(map[string]interface{}) + + if !reflect.DeepEqual(sysLog["enabled"], c.expected.enabled) { + t.Fatalf("Error matching output and expected for stdout enabled: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(sysLog["verbose_scan"], c.expected.verbose_scan) { + t.Fatalf("Error matching output and expected for stdout verbose_scan: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(sysLog["all_proc_events"], c.expected.all_proc_events) { + t.Fatalf("Error matching output and expected for stdout all_proc_events: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(sysLog["address"], c.expected.address) { + t.Fatalf("Error matching output and expected for stdout address: %#v vs %#v", out, c.expected) + } + + if !reflect.DeepEqual(sysLog["identifier"], c.expected.identifier) { + t.Fatalf("Error matching output and expected for stdout identifier: %#v vs %#v", out, c.expected) + } + } +} diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 401a6cd..b8dc9a0 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -19,19 +19,19 @@ Resources representing policies can become really long. It is often helpful to s variable "hosts" { default = [ { - name: "ec2-develop, + name: "ec2-development", compliance_check: [ { id: 16 } ] }, { - name: "ec2-staging, + name: "ec2-staging", compliance_check: [ { id: 16, block: true } ] }, { - name: "ec2-staging, + name: "ec2-production", compliance_check: [ { id: 16, block: true }, { id: 18, block: false }