diff --git a/CHANGELOG.md b/CHANGELOG.md index c93de56ca..22e986548 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add `tags` option to Kibana's SLOs ([#495](https://github.com/elastic/terraform-provider-elasticstack/pull/495)) - Add support for Authorization header - Bearer Token and ES-Client-Authentication fields added.([#500](https://github.com/elastic/terraform-provider-elasticstack/pull/500)) - Add support for managing Kibana Data Views ([#502](https://github.com/elastic/terraform-provider-elasticstack/pull/502)) +- Support Logstash SSL fields in Fleet output ([#498](https://github.com/elastic/terraform-provider-elasticstack/pull/498)) ### Fixed - Rename fleet package objects to `elasticstack_fleet_integration` and `elasticstack_fleet_integration_policy` ([#476](https://github.com/elastic/terraform-provider-elasticstack/pull/476)) diff --git a/docs/resources/fleet_output.md b/docs/resources/fleet_output.md index 221de9616..000193bd1 100644 --- a/docs/resources/fleet_output.md +++ b/docs/resources/fleet_output.md @@ -48,11 +48,24 @@ resource "elasticstack_fleet_output" "test_output" { - `default_monitoring` (Boolean) Make this output the default for agent monitoring. - `hosts` (List of String) A list of hosts. - `output_id` (String) Unique identifier of the output. +- `ssl` (Block List, Max: 1) SSL configuration. (see [below for nested schema](#nestedblock--ssl)) ### Read-Only - `id` (String) The ID of this resource. + +### Nested Schema for `ssl` + +Required: + +- `certificate` (String) Client SSL certificate. +- `key` (String, Sensitive) Client SSL certificate key. + +Optional: + +- `certificate_authorities` (List of String) Server SSL certificate authorities. + ## Import Import is supported using the following syntax: diff --git a/internal/fleet/output_resource.go b/internal/fleet/output_resource.go index aecc225d6..26ed3cc1c 100644 --- a/internal/fleet/output_resource.go +++ b/internal/fleet/output_resource.go @@ -57,6 +57,35 @@ func ResourceOutput() *schema.Resource { Type: schema.TypeBool, Optional: true, }, + "ssl": { + Description: "SSL configuration.", + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "certificate_authorities": { + Description: "Server SSL certificate authorities.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "certificate": { + Description: "Client SSL certificate.", + Type: schema.TypeString, + Required: true, + }, + "key": { + Description: "Client SSL certificate key.", + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + }, + }, + }, "config_yaml": { Description: "Advanced YAML configuration. YAML settings here will be added to the output section of each agent policy.", Type: schema.TypeString, @@ -174,6 +203,27 @@ func resourceOutputCreateLogstash(ctx context.Context, d *schema.ResourceData, m if value, ok := d.Get("ca_trusted_fingerprint").(string); ok && value != "" { reqData.CaTrustedFingerprint = &value } + if value, ok := d.GetOk("ssl"); ok { + ssl := value.([]interface{})[0].(map[string]interface{}) + reqData.Ssl = &struct { + Certificate *string `json:"certificate,omitempty"` + CertificateAuthorities *[]string `json:"certificate_authorities,omitempty"` + Key *string `json:"key,omitempty"` + }{} + if value, ok := ssl["certificate_authorities"].([]interface{}); ok { + certs := make([]string, len(value)) + for i, v := range value { + certs[i] = v.(string) + } + reqData.Ssl.CertificateAuthorities = &certs + } + if value, ok := ssl["certificate"].(string); ok { + reqData.Ssl.Certificate = &value + } + if value, ok := ssl["key"].(string); ok { + reqData.Ssl.Key = &value + } + } if value, ok := d.Get("config_yaml").(string); ok && value != "" { reqData.ConfigYaml = &value } @@ -299,6 +349,27 @@ func resourceOutputUpdateLogstash(ctx context.Context, d *schema.ResourceData, m if value, ok := d.Get("ca_sha256").(string); ok && value != "" { reqData.CaSha256 = &value } + if value, ok := d.GetOk("ssl"); ok { + ssl := value.([]interface{})[0].(map[string]interface{}) + reqData.Ssl = &struct { + Certificate *string `json:"certificate,omitempty"` + CertificateAuthorities *[]string `json:"certificate_authorities,omitempty"` + Key *string `json:"key,omitempty"` + }{} + if value, ok := ssl["certificate_authorities"].([]interface{}); ok { + certs := make([]string, len(value)) + for i, v := range value { + certs[i] = v.(string) + } + reqData.Ssl.CertificateAuthorities = &certs + } + if value, ok := ssl["certificate"].(string); ok { + reqData.Ssl.Certificate = &value + } + if value, ok := ssl["key"].(string); ok { + reqData.Ssl.Key = &value + } + } if value, ok := d.Get("config_yaml").(string); ok && value != "" { reqData.ConfigYaml = &value } @@ -394,6 +465,9 @@ func resourceOutputReadLogstash(d *schema.ResourceData, data fleetapi.OutputCrea return diag.FromErr(err) } } + if err := d.Set("ssl", flattenSslConfig(data)); err != nil { + return diag.FromErr(err) + } if data.ConfigYaml != nil { if err := d.Set("config_yaml", *data.ConfigYaml); err != nil { return diag.FromErr(err) @@ -456,3 +530,22 @@ func resourceOutputDelete(ctx context.Context, d *schema.ResourceData, meta inte return diags } + +func flattenSslConfig(data fleetapi.OutputCreateRequestLogstash) []interface{} { + if data.Ssl == nil { + return []interface{}{} + } + + ssl := make(map[string]interface{}) + if data.Ssl.CertificateAuthorities != nil { + ssl["certificate_authorities"] = *data.Ssl.CertificateAuthorities + } + if data.Ssl.Certificate != nil { + ssl["certificate"] = *data.Ssl.Certificate + } + if data.Ssl.Key != nil { + ssl["key"] = *data.Ssl.Key + } + + return []interface{}{ssl} +} diff --git a/internal/fleet/output_resource_test.go b/internal/fleet/output_resource_test.go index 9dd71a368..882e8b28e 100644 --- a/internal/fleet/output_resource_test.go +++ b/internal/fleet/output_resource_test.go @@ -71,6 +71,9 @@ func TestAccResourceOutputLogstash(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "default_integrations", "false"), resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "default_monitoring", "false"), resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "hosts.0", "logstash:5044"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.certificate_authorities.0", "placeholder"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.certificate", "placeholder"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.key", "placeholder"), ), }, { @@ -83,6 +86,9 @@ func TestAccResourceOutputLogstash(t *testing.T) { resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "default_integrations", "false"), resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "default_monitoring", "false"), resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "hosts.0", "logstash:5044"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.certificate_authorities.0", "placeholder"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.certificate", "placeholder"), + resource.TestCheckResourceAttr("elasticstack_fleet_output.test_output", "ssl.0.key", "placeholder"), ), }, }, @@ -152,6 +158,11 @@ resource "elasticstack_fleet_output" "test_output" { hosts = [ "logstash:5044" ] + ssl { + certificate_authorities = ["placeholder"] + certificate = "placeholder" + key = "placeholder" + } } `, fmt.Sprintf("Logstash Output %s", id)) } @@ -174,6 +185,11 @@ resource "elasticstack_fleet_output" "test_output" { hosts = [ "logstash:5044" ] + ssl { + certificate_authorities = ["placeholder"] + certificate = "placeholder" + key = "placeholder" + } } `, fmt.Sprintf("Updated Logstash Output %s", id))