Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- Add support for managing cross_cluster API keys in `elasticstack_elasticsearch_security_api_key` ([#1252](https://github.com/elastic/terraform-provider-elasticstack/pull/1252))
- Allow version changes without a destroy/create cycle with `elasticstack_fleet_integration` ([#1255](https://github.com/elastic/terraform-provider-elasticstack/pull/1255)). This fixes an issue where it was impossible to upgrade integrations which are used by an integration policy.
- Add `namespace` attribute to `elasticstack_kibana_synthetics_monitor` resource to support setting data stream namespace independently from `space_id` ([#1247](https://github.com/elastic/terraform-provider-elasticstack/pull/1247))
- Support setting an explit `connector_id` in `elasticstack_kibana_action_connector`. This attribute already existed, but was being ignored by the provider. Setting the attribute will return an error in Elastic Stack v8.8 and lower since creating a connector with an explicit ID is not supported. ([1260](https://github.com/elastic/terraform-provider-elasticstack/pull/1260))
- Migrate `elasticstack_kibana_action_connector` to the Terraform plugin framework ([#1269](https://github.com/elastic/terraform-provider-elasticstack/pull/1269))
- Migrate `elasticstack_elasticsearch_security_role_mapping` resource and data source to Terraform Plugin Framework ([#1279](https://github.com/elastic/terraform-provider-elasticstack/pull/1279))

## [0.11.17] - 2025-07-21
Expand Down
15 changes: 14 additions & 1 deletion docs/resources/kibana_action_connector.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,29 @@ resource "elasticstack_kibana_action_connector" "slack-api-connector" {

- `config` (String) The configuration for the connector. Configuration properties vary depending on the connector type.
- `connector_id` (String) A UUID v1 or v4 to use instead of a randomly generated ID.
- `kibana_connection` (Block List) Kibana connection configuration block. (see [below for nested schema](#nestedblock--kibana_connection))
- `secrets` (String, Sensitive) The secrets configuration for the connector. Secrets configuration properties vary depending on the connector type.
- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used.

### Read-Only

- `id` (String) The ID of this resource.
- `id` (String) Internal identifier of the resource.
- `is_deprecated` (Boolean) Indicates whether the connector type is deprecated.
- `is_missing_secrets` (Boolean) Indicates whether secrets are missing for the connector.
- `is_preconfigured` (Boolean) Indicates whether it is a preconfigured connector.

<a id="nestedblock--kibana_connection"></a>
### Nested Schema for `kibana_connection`

Optional:

- `api_key` (String, Sensitive) API Key to use for authentication to Kibana
- `ca_certs` (List of String) A list of paths to CA certificates to validate the certificate presented by the Kibana server.
- `endpoints` (List of String, Sensitive) A comma-separated list of endpoints where the terraform provider will point to, this must include the http(s) schema and port number.
- `insecure` (Boolean) Disable TLS certificate validation
- `password` (String, Sensitive) Password to use for API authentication to Kibana.
- `username` (String) Username to use for API authentication to Kibana.

## Import

Import is supported using the following syntax:
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.5.0
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/terraform-plugin-framework v1.15.1
github.com/hashicorp/terraform-plugin-framework-jsontypes v0.2.0
Expand Down Expand Up @@ -217,7 +218,6 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.6.3 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hc-install v0.9.2 // indirect
github.com/hashicorp/hcl/v2 v2.23.0 // indirect
Expand Down
115 changes: 47 additions & 68 deletions internal/clients/kibana_oapi/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/models"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
fwdiag "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
sdkdiag "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
)

func CreateConnector(ctx context.Context, client *Client, connectorOld models.KibanaActionConnector) (string, diag.Diagnostics) {
func CreateConnector(ctx context.Context, client *Client, connectorOld models.KibanaActionConnector) (string, fwdiag.Diagnostics) {
body, err := createConnectorRequestBody(connectorOld)
if err != nil {
return "", diag.FromErr(err)
return "", fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Failed to create connector request body", err.Error())}
}

resp, err := client.API.PostActionsConnectorIdWithResponse(
Expand All @@ -35,40 +36,40 @@ func CreateConnector(ctx context.Context, client *Client, connectorOld models.Ki
},
)
if err != nil {
return "", diag.FromErr(err)
return "", fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("HTTP request failed", err.Error())}
}

switch resp.StatusCode() {
case http.StatusOK:
return resp.JSON200.Id, nil
default:
return "", reportUnknownErrorSDK(resp.StatusCode(), resp.Body)
return "", reportUnknownError(resp.StatusCode(), resp.Body)
}
}

func UpdateConnector(ctx context.Context, client *Client, connectorOld models.KibanaActionConnector) (string, diag.Diagnostics) {
func UpdateConnector(ctx context.Context, client *Client, connectorOld models.KibanaActionConnector) (string, fwdiag.Diagnostics) {
body, err := updateConnectorRequestBody(connectorOld)
if err != nil {
return "", diag.FromErr(err)
return "", fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Failed to create update request body", err.Error())}
}

resp, err := client.API.PutActionsConnectorIdWithResponse(ctx, connectorOld.SpaceID, connectorOld.ConnectorID, body)
if err != nil {
return "", diag.Errorf("unable to update connector: [%v]", err)
return "", fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Unable to update connector", err.Error())}
}

switch resp.StatusCode() {
case http.StatusOK:
return resp.JSON200.Id, nil
default:
return "", reportUnknownErrorSDK(resp.StatusCode(), resp.Body)
return "", reportUnknownError(resp.StatusCode(), resp.Body)
}
}

func GetConnector(ctx context.Context, client *Client, connectorID, spaceID string) (*models.KibanaActionConnector, diag.Diagnostics) {
func GetConnector(ctx context.Context, client *Client, connectorID, spaceID string) (*models.KibanaActionConnector, fwdiag.Diagnostics) {
resp, err := client.API.GetActionsConnectorIdWithResponse(ctx, spaceID, connectorID)
if err != nil {
return nil, diag.Errorf("unable to get connector: [%v]", err)
return nil, fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Unable to get connector", err.Error())}
}

switch resp.StatusCode() {
Expand All @@ -77,14 +78,14 @@ func GetConnector(ctx context.Context, client *Client, connectorID, spaceID stri
case http.StatusNotFound:
return nil, nil
default:
return nil, reportUnknownErrorSDK(resp.StatusCode(), resp.Body)
return nil, reportUnknownError(resp.StatusCode(), resp.Body)
}
}

func SearchConnectors(ctx context.Context, client *Client, connectorName, spaceID, connectorTypeID string) ([]*models.KibanaActionConnector, diag.Diagnostics) {
func SearchConnectors(ctx context.Context, client *Client, connectorName, spaceID, connectorTypeID string) ([]*models.KibanaActionConnector, sdkdiag.Diagnostics) {
resp, err := client.API.GetActionsConnectorsWithResponse(ctx, spaceID)
if err != nil {
return nil, diag.Errorf("unable to get connectors: [%v]", err)
return nil, sdkdiag.Errorf("unable to get connectors: [%v]", err)
}

if resp.StatusCode() != http.StatusOK {
Expand All @@ -101,9 +102,9 @@ func SearchConnectors(ctx context.Context, client *Client, connectorName, spaceI
continue
}

c, diags := ConnectorResponseToModel(spaceID, &connector)
if diags.HasError() {
return nil, diags
c, fwDiags := ConnectorResponseToModel(spaceID, &connector)
if fwDiags.HasError() {
return nil, utils.SDKDiagsFromFramework(fwDiags)
}

foundConnectors = append(foundConnectors, c)
Expand All @@ -115,9 +116,9 @@ func SearchConnectors(ctx context.Context, client *Client, connectorName, spaceI
return foundConnectors, nil
}

func ConnectorResponseToModel(spaceID string, connector *kbapi.ConnectorResponse) (*models.KibanaActionConnector, diag.Diagnostics) {
func ConnectorResponseToModel(spaceID string, connector *kbapi.ConnectorResponse) (*models.KibanaActionConnector, fwdiag.Diagnostics) {
if connector == nil {
return nil, diag.Errorf("connector response is nil")
return nil, fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Invalid connector response", "connector response is nil")}
}

var configJSON []byte
Expand All @@ -132,7 +133,7 @@ func ConnectorResponseToModel(spaceID string, connector *kbapi.ConnectorResponse
var err error
configJSON, err = json.Marshal(configMap)
if err != nil {
return nil, diag.Errorf("unable to marshal config: %v", err)
return nil, fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Unable to marshal config", err.Error())}
}

// If we have a specific config type, marshal into and out of that to
Expand All @@ -141,7 +142,7 @@ func ConnectorResponseToModel(spaceID string, connector *kbapi.ConnectorResponse
if ok {
configJSONString, err := handler.remarshalConfig(string(configJSON))
if err != nil {
return nil, diag.Errorf("failed to remarshal config: %v", err)
return nil, fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Failed to remarshal config", err.Error())}
}

configJSON = []byte(configJSONString)
Expand All @@ -165,21 +166,21 @@ func ConnectorResponseToModel(spaceID string, connector *kbapi.ConnectorResponse
return model, nil
}

func DeleteConnector(ctx context.Context, client *Client, connectorID string, spaceID string) diag.Diagnostics {
func DeleteConnector(ctx context.Context, client *Client, connectorID string, spaceID string) fwdiag.Diagnostics {
resp, err := client.API.DeleteActionsConnectorIdWithResponse(ctx, spaceID, connectorID)
if err != nil {
return diag.Errorf("unable to delete connector: [%v]", err)
return fwdiag.Diagnostics{fwdiag.NewErrorDiagnostic("Unable to delete connector", err.Error())}
}

if resp.StatusCode() != http.StatusOK && resp.StatusCode() != http.StatusNoContent {
return reportUnknownErrorSDK(resp.StatusCode(), resp.Body)
return reportUnknownError(resp.StatusCode(), resp.Body)
}

return nil
}

type connectorConfigHandler struct {
defaults func(plan, backend string) (string, error)
defaults func(plan string) (string, error)
remarshalConfig func(config string) (string, error)
}

Expand All @@ -193,7 +194,6 @@ var connectorConfigHandlers = map[string]connectorConfigHandler{
remarshalConfig: remarshalConfig[kbapi.EmailConfig],
},
".gemini": {
defaults: connectorConfigWithDefaultsGemini,
remarshalConfig: remarshalConfig[kbapi.GeminiConfig],
},
".index": {
Expand All @@ -205,15 +205,13 @@ var connectorConfigHandlers = map[string]connectorConfigHandler{
remarshalConfig: remarshalConfig[kbapi.JiraConfig],
},
".opsgenie": {
defaults: connectorConfigWithDefaultsOpsgenie,
remarshalConfig: remarshalConfig[kbapi.OpsgenieConfig],
},
".pagerduty": {
defaults: connectorConfigWithDefaultsPagerduty,
remarshalConfig: remarshalConfig[kbapi.PagerdutyConfig],
},
".resilient": {
defaults: connectorConfigWithDefaultsResilient,
remarshalConfig: remarshalConfig[kbapi.ResilientConfig],
},
".servicenow": {
Expand All @@ -228,16 +226,17 @@ var connectorConfigHandlers = map[string]connectorConfigHandler{
defaults: connectorConfigWithDefaultsServicenowSir,
remarshalConfig: remarshalConfig[kbapi.ServicenowConfig],
},
".slack_api": {
remarshalConfig: remarshalConfig[kbapi.SlackApiConfig],
},
".swimlane": {
defaults: connectorConfigWithDefaultsSwimlane,
remarshalConfig: remarshalConfig[kbapi.SwimlaneConfig],
},
".tines": {
defaults: connectorConfigWithDefaultsTines,
remarshalConfig: remarshalConfig[kbapi.TinesConfig],
},
".webhook": {
defaults: connectorConfigWithDefaultsWebhook,
remarshalConfig: remarshalConfig[kbapi.WebhookConfig],
},
".xmatters": {
Expand All @@ -246,13 +245,17 @@ var connectorConfigHandlers = map[string]connectorConfigHandler{
},
}

func ConnectorConfigWithDefaults(connectorTypeID, plan, backend, state string) (string, error) {
func ConnectorConfigWithDefaults(connectorTypeID, plan string) (string, error) {
handler, ok := connectorConfigHandlers[connectorTypeID]
if !ok {
return plan, errors.New("unknown connector type ID: " + connectorTypeID)
}

return handler.defaults(plan, backend)
if handler.defaults == nil {
return plan, nil
}

return handler.defaults(plan)
}

// User can omit optonal fields in config JSON.
Expand All @@ -271,7 +274,7 @@ func remarshalConfig[T any](plan string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsCasesWebhook(plan, _ string) (string, error) {
func connectorConfigWithDefaultsCasesWebhook(plan string) (string, error) {
var custom kbapi.CasesWebhookConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand All @@ -292,7 +295,7 @@ func connectorConfigWithDefaultsCasesWebhook(plan, _ string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsEmail(plan, _ string) (string, error) {
func connectorConfigWithDefaultsEmail(plan string) (string, error) {
var custom kbapi.EmailConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand All @@ -310,11 +313,7 @@ func connectorConfigWithDefaultsEmail(plan, _ string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsGemini(plan, _ string) (string, error) {
return plan, nil
}

func connectorConfigWithDefaultsIndex(plan, _ string) (string, error) {
func connectorConfigWithDefaultsIndex(plan string) (string, error) {
var custom kbapi.IndexConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand All @@ -329,32 +328,20 @@ func connectorConfigWithDefaultsIndex(plan, _ string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsJira(plan, _ string) (string, error) {
func connectorConfigWithDefaultsJira(plan string) (string, error) {
return remarshalConfig[kbapi.JiraConfig](plan)
}

func connectorConfigWithDefaultsOpsgenie(plan, _ string) (string, error) {
return plan, nil
}

func connectorConfigWithDefaultsPagerduty(plan, _ string) (string, error) {
func connectorConfigWithDefaultsPagerduty(plan string) (string, error) {
return remarshalConfig[kbapi.PagerdutyConfig](plan)
}

func connectorConfigWithDefaultsResilient(plan, _ string) (string, error) {
return plan, nil
}

func connectorConfigWithDefaultsServicenow(plan, backend string) (string, error) {
func connectorConfigWithDefaultsServicenow(plan string) (string, error) {
var planConfig kbapi.ServicenowConfig
if err := json.Unmarshal([]byte(plan), &planConfig); err != nil {
return "", err
}
var backendConfig kbapi.ServicenowConfig
if err := json.Unmarshal([]byte(backend), &backendConfig); err != nil {
return "", err
}
if planConfig.IsOAuth == nil && backendConfig.IsOAuth != nil && !*backendConfig.IsOAuth {
if planConfig.IsOAuth == nil {
planConfig.IsOAuth = utils.Pointer(false)
}
if planConfig.UsesTableApi == nil {
Expand All @@ -367,7 +354,7 @@ func connectorConfigWithDefaultsServicenow(plan, backend string) (string, error)
return string(customJSON), nil
}

func connectorConfigWithDefaultsServicenowItom(plan, _ string) (string, error) {
func connectorConfigWithDefaultsServicenowItom(plan string) (string, error) {
var custom kbapi.ServicenowItomConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand All @@ -382,11 +369,11 @@ func connectorConfigWithDefaultsServicenowItom(plan, _ string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsServicenowSir(plan, backend string) (string, error) {
return connectorConfigWithDefaultsServicenow(plan, backend)
func connectorConfigWithDefaultsServicenowSir(plan string) (string, error) {
return connectorConfigWithDefaultsServicenow(plan)
}

func connectorConfigWithDefaultsSwimlane(plan, _ string) (string, error) {
func connectorConfigWithDefaultsSwimlane(plan string) (string, error) {
var custom kbapi.SwimlaneConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand Down Expand Up @@ -444,15 +431,7 @@ func connectorConfigWithDefaultsSwimlane(plan, _ string) (string, error) {
return string(customJSON), nil
}

func connectorConfigWithDefaultsTines(plan, _ string) (string, error) {
return plan, nil
}

func connectorConfigWithDefaultsWebhook(plan, _ string) (string, error) {
return plan, nil
}

func connectorConfigWithDefaultsXmatters(plan, _ string) (string, error) {
func connectorConfigWithDefaultsXmatters(plan string) (string, error) {
var custom kbapi.XmattersConfig
if err := json.Unmarshal([]byte(plan), &custom); err != nil {
return "", err
Expand Down
Loading