diff --git a/.changelog/1081.txt b/.changelog/1081.txt new file mode 100644 index 000000000..91f0a2782 --- /dev/null +++ b/.changelog/1081.txt @@ -0,0 +1,3 @@ +```release-note:feature +add vault_secrets_integration_twilio resource +``` diff --git a/docs/resources/vault_secrets_integration_twilio.md b/docs/resources/vault_secrets_integration_twilio.md new file mode 100644 index 000000000..96a307057 --- /dev/null +++ b/docs/resources/vault_secrets_integration_twilio.md @@ -0,0 +1,64 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "hcp_vault_secrets_integration_twilio Resource - terraform-provider-hcp" +subcategory: "" +description: |- + The Vault Secrets Twilio integration resource manages a Twilio integration. +--- + +# hcp_vault_secrets_integration_twilio (Resource) + +The Vault Secrets Twilio integration resource manages a Twilio integration. + +## Example Usage + +```terraform +resource "hcp_vault_secrets_integration_twilio" "example" { + name = "my-twilio-1" + capabilities = ["ROTATION"] + static_credential_details = { + account_sid = "AC7..." + api_key_sid = "TKa..." + api_key_secret = "6aG..." + } +} +``` + + +## Schema + +### Required + +- `capabilities` (Set of String) Capabilities enabled for the integration. See the Vault Secrets documentation for the list of supported capabilities per provider. +- `name` (String) The Vault Secrets integration name. + +### Optional + +- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted. +- `static_credential_details` (Attributes) Twilio API key parts used to authenticate against the target Twilio account. (see [below for nested schema](#nestedatt--static_credential_details)) + +### Read-Only + +- `organization_id` (String) HCP organization ID that owns the HCP Vault Secrets integration. +- `resource_id` (String) Resource ID used to uniquely identify the integration instance on the HCP platform. +- `resource_name` (String) Resource name used to uniquely identify the integration instance on the HCP platform. + + +### Nested Schema for `static_credential_details` + +Required: + +- `account_sid` (String) Account SID for the target Twilio account. +- `api_key_secret` (String, Sensitive) Api key secret used with the api key SID to authenticate against the target Twilio account. +- `api_key_sid` (String) Api key SID to authenticate against the target Twilio account. + +## Import + +Import is supported using the following syntax: + +```shell +# Vault Secrets Twilio Integration can be imported by specifying the name of the integration +# Note that since the Api Key secret is never returned on the Vault Secrets API, +# the next plan or apply will show a diff for that field. +terraform import hcp_vault_secrets_integration_twilio.example my-twilio-1 +``` diff --git a/examples/resources/hcp_vault_secrets_integration_twilio/import.sh b/examples/resources/hcp_vault_secrets_integration_twilio/import.sh new file mode 100644 index 000000000..c7b740f58 --- /dev/null +++ b/examples/resources/hcp_vault_secrets_integration_twilio/import.sh @@ -0,0 +1,4 @@ +# Vault Secrets Twilio Integration can be imported by specifying the name of the integration +# Note that since the Api Key secret is never returned on the Vault Secrets API, +# the next plan or apply will show a diff for that field. +terraform import hcp_vault_secrets_integration_twilio.example my-twilio-1 diff --git a/examples/resources/hcp_vault_secrets_integration_twilio/resource.tf b/examples/resources/hcp_vault_secrets_integration_twilio/resource.tf new file mode 100644 index 000000000..72ba89e06 --- /dev/null +++ b/examples/resources/hcp_vault_secrets_integration_twilio/resource.tf @@ -0,0 +1,9 @@ +resource "hcp_vault_secrets_integration_twilio" "example" { + name = "my-twilio-1" + capabilities = ["ROTATION"] + static_credential_details = { + account_sid = "AC7..." + api_key_sid = "TKa..." + api_key_secret = "6aG..." + } +} \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 80138dc2b..7242f699d 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -154,6 +154,7 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res vaultsecrets.NewVaultSecretsAppIAMPolicyResource, vaultsecrets.NewVaultSecretsAppIAMBindingResource, vaultsecrets.NewVaultSecretsIntegrationAWSResource, + vaultsecrets.NewVaultSecretsIntegrationTwilioResource, // IAM iam.NewServicePrincipalResource, iam.NewServicePrincipalKeyResource, diff --git a/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio.go b/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio.go new file mode 100644 index 000000000..0fcc5c6e6 --- /dev/null +++ b/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio.go @@ -0,0 +1,303 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vaultsecrets + +import ( + "context" + "fmt" + + "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" + secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-provider-hcp/internal/clients" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/modifiers" + "golang.org/x/exp/maps" +) + +type IntegrationTwilio struct { + // Input fields + ProjectID types.String `tfsdk:"project_id"` + Name types.String `tfsdk:"name"` + Capabilities types.Set `tfsdk:"capabilities"` + StaticCredentialDetails types.Object `tfsdk:"static_credential_details"` + + // Computed fields + OrganizationID types.String `tfsdk:"organization_id"` + ResourceID types.String `tfsdk:"resource_id"` + ResourceName types.String `tfsdk:"resource_name"` + + // Inner API-compatible models derived from the Terraform fields + capabilities []*secretmodels.Secrets20231128Capability `tfsdk:"-"` + staticCredentialDetails *secretmodels.Secrets20231128TwilioStaticCredentialsRequest `tfsdk:"-"` +} + +// Helper structs to help populate concrete targets from types.Object fields +type staticCredentialDetails struct { + AccountSID types.String `tfsdk:"account_sid"` + APIKeySID types.String `tfsdk:"api_key_sid"` + APIKeySecret types.String `tfsdk:"api_key_secret"` +} + +var _ resource.Resource = &resourceVaultSecretsIntegrationTwilio{} +var _ resource.ResourceWithConfigure = &resourceVaultSecretsIntegrationTwilio{} +var _ resource.ResourceWithModifyPlan = &resourceVaultSecretsIntegrationTwilio{} +var _ resource.ResourceWithImportState = &resourceVaultSecretsIntegrationTwilio{} + +func NewVaultSecretsIntegrationTwilioResource() resource.Resource { + return &resourceVaultSecretsIntegrationTwilio{} +} + +type resourceVaultSecretsIntegrationTwilio struct { + client *clients.Client +} + +func (r *resourceVaultSecretsIntegrationTwilio) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_vault_secrets_integration_twilio" +} + +func (r *resourceVaultSecretsIntegrationTwilio) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + attributes := map[string]schema.Attribute{ + "static_credential_details": schema.SingleNestedAttribute{ + Description: "Twilio API key parts used to authenticate against the target Twilio account.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "account_sid": schema.StringAttribute{ + Description: "Account SID for the target Twilio account.", + Required: true, + }, + "api_key_sid": schema.StringAttribute{ + Description: "Api key SID to authenticate against the target Twilio account.", + Required: true, + }, + "api_key_secret": schema.StringAttribute{ + Description: "Api key secret used with the api key SID to authenticate against the target Twilio account.", + Required: true, + Sensitive: true, + }, + }, + }, + } + + maps.Copy(attributes, sharedIntegrationAttributes) + + resp.Schema = schema.Schema{ + MarkdownDescription: "The Vault Secrets Twilio integration resource manages a Twilio integration.", + Attributes: attributes, + } +} + +func (r *resourceVaultSecretsIntegrationTwilio) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + client, ok := req.ProviderData.(*clients.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *clients.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + r.client = client +} + +func (r *resourceVaultSecretsIntegrationTwilio) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + modifiers.ModifyPlanForDefaultProjectChange(ctx, r.client.Config.ProjectID, req.State, req.Config, req.Plan, resp) +} + +func (r *resourceVaultSecretsIntegrationTwilio) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + resp.Diagnostics.Append(decorateOperation[*IntegrationTwilio](ctx, r.client, &resp.State, req.State.Get, "reading", func(i integration) (any, error) { + integration, ok := i.(*IntegrationTwilio) + if !ok { + return nil, fmt.Errorf("invalid integration type, expected *IntegrationTwilio, got: %T, this is a bug on the provider", i) + } + + response, err := r.client.VaultSecretsPreview.GetTwilioIntegration( + secret_service.NewGetTwilioIntegrationParamsWithContext(ctx). + WithOrganizationID(integration.OrganizationID.ValueString()). + WithProjectID(integration.ProjectID.ValueString()). + WithName(integration.Name.ValueString()), nil) + if err != nil && !clients.IsResponseCodeNotFound(err) { + return nil, err + } + if response == nil || response.Payload == nil { + return nil, nil + } + return response.Payload.Integration, nil + })...) +} + +func (r *resourceVaultSecretsIntegrationTwilio) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + resp.Diagnostics.Append(decorateOperation[*IntegrationTwilio](ctx, r.client, &resp.State, req.Plan.Get, "creating", func(i integration) (any, error) { + integration, ok := i.(*IntegrationTwilio) + if !ok { + return nil, fmt.Errorf("invalid integration type, expected *IntegrationTwilio, got: %T, this is a bug on the provider", i) + } + + response, err := r.client.VaultSecretsPreview.CreateTwilioIntegration(&secret_service.CreateTwilioIntegrationParams{ + Body: &secretmodels.SecretServiceCreateTwilioIntegrationBody{ + Capabilities: integration.capabilities, + StaticCredentialDetails: integration.staticCredentialDetails, + Name: integration.Name.ValueString(), + }, + OrganizationID: integration.OrganizationID.ValueString(), + ProjectID: integration.ProjectID.ValueString(), + }, nil) + if err != nil && !clients.IsResponseCodeNotFound(err) { + return nil, err + } + if response == nil || response.Payload == nil { + return nil, nil + } + return response.Payload.Integration, nil + })...) +} + +func (r *resourceVaultSecretsIntegrationTwilio) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + resp.Diagnostics.Append(decorateOperation[*IntegrationTwilio](ctx, r.client, &resp.State, req.Plan.Get, "updating", func(i integration) (any, error) { + integration, ok := i.(*IntegrationTwilio) + if !ok { + return nil, fmt.Errorf("invalid integration type, expected *IntegrationTwilio, got: %T, this is a bug on the provider", i) + } + + response, err := r.client.VaultSecretsPreview.UpdateTwilioIntegration(&secret_service.UpdateTwilioIntegrationParams{ + Body: &secretmodels.SecretServiceUpdateTwilioIntegrationBody{ + Capabilities: integration.capabilities, + StaticCredentialDetails: integration.staticCredentialDetails, + }, + Name: integration.Name.ValueString(), + OrganizationID: integration.OrganizationID.ValueString(), + ProjectID: integration.ProjectID.ValueString(), + }, nil) + if err != nil && !clients.IsResponseCodeNotFound(err) { + return nil, err + } + if response == nil || response.Payload == nil { + return nil, nil + } + return response.Payload.Integration, nil + })...) +} + +func (r *resourceVaultSecretsIntegrationTwilio) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + resp.Diagnostics.Append(decorateOperation[*IntegrationTwilio](ctx, r.client, &resp.State, req.State.Get, "deleting", func(i integration) (any, error) { + integration, ok := i.(*IntegrationTwilio) + if !ok { + return nil, fmt.Errorf("invalid integration type, expected *IntegrationTwilio, got: %T, this is a bug on the provider", i) + } + + _, err := r.client.VaultSecretsPreview.DeleteTwilioIntegration( + secret_service.NewDeleteTwilioIntegrationParamsWithContext(ctx). + WithOrganizationID(integration.OrganizationID.ValueString()). + WithProjectID(integration.ProjectID.ValueString()). + WithName(integration.Name.ValueString()), nil) + if err != nil && !clients.IsResponseCodeNotFound(err) { + return nil, err + } + return nil, nil + })...) +} + +func (r *resourceVaultSecretsIntegrationTwilio) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // The Vault Secrets API does not return sensitive values like the secret access key, so they will be initialized to an empty value + // It means the first plan/apply after a successful import will always show a diff for the secret access key + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("organization_id"), r.client.Config.OrganizationID)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), r.client.Config.ProjectID)...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name"), req.ID)...) +} + +var _ integration = &IntegrationTwilio{} + +func (i *IntegrationTwilio) projectID() types.String { + return i.ProjectID + +} +func (i *IntegrationTwilio) initModel(ctx context.Context, orgID, projID string) diag.Diagnostics { + // Init fields that depend on the Terraform provider configuration + i.OrganizationID = types.StringValue(orgID) + i.ProjectID = types.StringValue(projID) + + // Init the HVS domain models from the Terraform domain models + var capabilities []types.String + diags := i.Capabilities.ElementsAs(ctx, &capabilities, false) + if diags.HasError() { + return diags + } + for _, c := range capabilities { + i.capabilities = append(i.capabilities, secretmodels.Secrets20231128Capability(c.ValueString()).Pointer()) + } + + if !i.StaticCredentialDetails.IsNull() { + scd := staticCredentialDetails{} + diags = i.StaticCredentialDetails.As(ctx, &scd, basetypes.ObjectAsOptions{}) + if diags.HasError() { + return diags + } + + i.staticCredentialDetails = &secretmodels.Secrets20231128TwilioStaticCredentialsRequest{ + AccountSid: scd.AccountSID.ValueString(), + APIKeySecret: scd.APIKeySecret.ValueString(), + APIKeySid: scd.APIKeySID.ValueString(), + } + } + + return diag.Diagnostics{} +} + +func (i *IntegrationTwilio) fromModel(ctx context.Context, orgID, projID string, model any) diag.Diagnostics { + diags := diag.Diagnostics{} + + integrationModel, ok := model.(*secretmodels.Secrets20231128TwilioIntegration) + if !ok { + diags.AddError("Invalid model type, this is a bug on the provider.", fmt.Sprintf("Expected *secretmodels.Secrets20231128TwilioIntegration, got: %T", model)) + return diags + } + + i.OrganizationID = types.StringValue(orgID) + i.ProjectID = types.StringValue(projID) + // TODO These fields are not returned by the API on updates, so we use the state value if they are blank on the model as a stopgap until it gets fixed on HVS + if integrationModel.ResourceID != "" { + i.ResourceID = types.StringValue(integrationModel.ResourceID) + } + if integrationModel.ResourceName != "" { + i.ResourceName = types.StringValue(integrationModel.ResourceName) + } + if integrationModel.Name != "" { + i.Name = types.StringValue(integrationModel.Name) + } + + var values []attr.Value + for _, c := range integrationModel.Capabilities { + values = append(values, types.StringValue(string(*c))) + } + i.Capabilities, diags = types.SetValue(types.StringType, values) + if diags.HasError() { + return diags + } + + if integrationModel.StaticCredentialDetails != nil { + // The secret key is not returned by the API, so we use an empty value (e.g. for imports) or the state value (e.g. for updates) + apiKeySecret := "" + if i.staticCredentialDetails != nil { + apiKeySecret = i.staticCredentialDetails.APIKeySecret + } + + i.StaticCredentialDetails, diags = types.ObjectValue(i.StaticCredentialDetails.AttributeTypes(ctx), map[string]attr.Value{ + "account_sid": types.StringValue(integrationModel.StaticCredentialDetails.AccountSid), + "api_key_sid": types.StringValue(integrationModel.StaticCredentialDetails.APIKeySid), + "api_key_secret": types.StringValue(apiKeySecret), + }) + if diags.HasError() { + return diags + } + } + + return diags +} diff --git a/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio_test.go b/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio_test.go new file mode 100644 index 000000000..a3fc9b617 --- /dev/null +++ b/internal/provider/vaultsecrets/resource_vault_secrets_integration_twilio_test.go @@ -0,0 +1,156 @@ +package vaultsecrets_test + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/client/secret_service" + secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/preview/2023-11-28/models" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-hcp/internal/clients" + "github.com/hashicorp/terraform-provider-hcp/internal/provider/acctest" +) + +func TestAccVaultSecretsResourceIntegrationTwilio(t *testing.T) { + accountSID := checkRequiredEnvVarOrFail(t, "TWILIO_ACCOUNT_SID") + apiKeySID := checkRequiredEnvVarOrFail(t, "TWILIO_API_KEY_SID") + apiKeySecret := checkRequiredEnvVarOrFail(t, "TWILIO_API_KEY_SECRET") + + integrationName1 := generateRandomSlug() + integrationName2 := generateRandomSlug() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create initial integration with access keys + { + Config: config(integrationName1, accountSID, apiKeySID, apiKeySecret), + Check: resource.ComposeTestCheckFunc( + checkFuncs(integrationName1, accountSID, apiKeySID, apiKeySecret)..., + ), + }, + // Changing the name forces a recreation + { + Config: config(integrationName2, accountSID, apiKeySID, apiKeySecret), + Check: resource.ComposeTestCheckFunc( + checkFuncs(integrationName2, accountSID, apiKeySID, apiKeySecret)..., + ), + }, + // Modifying mutable fields causes an update + { + Config: config(integrationName2, accountSID, apiKeySID, apiKeySecret), + Check: resource.ComposeTestCheckFunc( + checkFuncs(integrationName2, accountSID, apiKeySID, apiKeySecret)..., + ), + }, + // Deleting the integration out of band causes a recreation + { + PreConfig: func() { + t.Helper() + client := acctest.HCPClients(t) + _, err := client.VaultSecretsPreview.DeleteTwilioIntegration(&secret_service.DeleteTwilioIntegrationParams{ + Name: integrationName2, + OrganizationID: client.Config.OrganizationID, + ProjectID: client.Config.ProjectID, + }, nil) + if err != nil { + t.Fatal(err) + } + }, + Config: config(integrationName2, accountSID, apiKeySID, apiKeySecret), + Check: resource.ComposeTestCheckFunc( + checkFuncs(integrationName2, accountSID, apiKeySID, apiKeySecret)..., + ), + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + // Pre-existing integration can be imported + { + PreConfig: func() { + t.Helper() + client := acctest.HCPClients(t) + _, err := client.VaultSecretsPreview.CreateTwilioIntegration(&secret_service.CreateTwilioIntegrationParams{ + Body: &secretmodels.SecretServiceCreateTwilioIntegrationBody{ + Capabilities: []*secretmodels.Secrets20231128Capability{secretmodels.Secrets20231128CapabilityROTATION.Pointer()}, + StaticCredentialDetails: &secretmodels.Secrets20231128TwilioStaticCredentialsRequest{ + AccountSid: accountSID, + APIKeySid: apiKeySID, + APIKeySecret: apiKeySecret, + }, + Name: integrationName2, + }, + OrganizationID: client.Config.OrganizationID, + ProjectID: client.Config.ProjectID, + }, nil) + if err != nil { + t.Fatal(err) + } + }, + Config: config(integrationName2, accountSID, apiKeySID, apiKeySecret), + Check: resource.ComposeTestCheckFunc( + checkFuncs(integrationName2, accountSID, apiKeySID, apiKeySecret)..., + ), + ResourceName: "hcp_vault_secrets_integration_twilio.acc_test", + ImportStateId: integrationName2, + ImportState: true, + }, + }, + CheckDestroy: func(_ *terraform.State) error { + if twilioIntegrationExists(t, integrationName1) { + return fmt.Errorf("test twilio integration %s was not destroyed", integrationName1) + } + if twilioIntegrationExists(t, integrationName2) { + return fmt.Errorf("test twilio integration %s was not destroyed", integrationName2) + } + return nil + }, + }) +} + +func config(integrationName, accountSID, apiKeySID, apiKeySecret string) string { + return fmt.Sprintf(` + resource "hcp_vault_secrets_integration_twilio" "acc_test" { + name = %q + capabilities = ["ROTATION"] + static_credential_details = { + account_sid = %q + api_key_sid = %q + api_key_secret = %q + } + }`, integrationName, accountSID, apiKeySID, apiKeySecret) +} + +func checkFuncs(integrationName, accountSID, apiKeySID, apiKeySecret string) []resource.TestCheckFunc { + return []resource.TestCheckFunc{ + resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration_twilio.acc_test", "organization_id"), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "project_id", os.Getenv("HCP_PROJECT_ID")), + resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration_twilio.acc_test", "resource_id"), + resource.TestCheckResourceAttrSet("hcp_vault_secrets_integration_twilio.acc_test", "resource_name"), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "name", integrationName), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "capabilities.#", "1"), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "capabilities.0", "ROTATION"), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "static_credential_details.account_sid", accountSID), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "static_credential_details.api_key_secret", apiKeySecret), + resource.TestCheckResourceAttr("hcp_vault_secrets_integration_twilio.acc_test", "static_credential_details.api_key_sid", apiKeySID), + } +} + +func twilioIntegrationExists(t *testing.T, name string) bool { + t.Helper() + + client := acctest.HCPClients(t) + + response, err := client.VaultSecretsPreview.GetTwilioIntegration( + secret_service.NewGetTwilioIntegrationParamsWithContext(ctx). + WithOrganizationID(client.Config.OrganizationID). + WithProjectID(client.Config.ProjectID). + WithName(name), nil) + if err != nil && !clients.IsResponseCodeNotFound(err) { + t.Fatal(err) + } + + return !clients.IsResponseCodeNotFound(err) && response != nil && response.Payload != nil && response.Payload.Integration != nil +}