diff --git a/.changelog/575.txt b/.changelog/575.txt
new file mode 100644
index 000000000..dc306ee4b
--- /dev/null
+++ b/.changelog/575.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+Add `vault_plugin` resource as optional subresource for `hcp_vault_cluster`
+```
diff --git a/docs/data-sources/vault_cluster.md b/docs/data-sources/vault_cluster.md
index 03e16b1cf..478458924 100644
--- a/docs/data-sources/vault_cluster.md
+++ b/docs/data-sources/vault_cluster.md
@@ -31,6 +31,7 @@ data "hcp_vault_cluster" "example" {
If not specified, the project specified in the HCP Provider config block will be used, if configured.
If a project is not configured in the HCP Provider config block, the oldest project in the organization will be used.
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
+- `vault_plugin` (Block List) The external plugins to install on the vault cluster (see [below for nested schema](#nestedblock--vault_plugin))
### Read-Only
@@ -63,6 +64,15 @@ Optional:
- `default` (String)
+
+### Nested Schema for `vault_plugin`
+
+Required:
+
+- `plugin_name` (String) The name of the plugin
+- `plugin_type` (String) The type of the plugin
+
+
### Nested Schema for `audit_log_config`
diff --git a/docs/resources/vault_cluster.md b/docs/resources/vault_cluster.md
index 18ca644ee..b3f0d8cd4 100644
--- a/docs/resources/vault_cluster.md
+++ b/docs/resources/vault_cluster.md
@@ -61,6 +61,7 @@ If a project is not configured in the HCP Provider config block, the oldest proj
- `public_endpoint` (Boolean) Denotes that the cluster has a public endpoint. Defaults to false.
- `tier` (String) Tier of the HCP Vault cluster. Valid options for tiers - `dev`, `starter_small`, `standard_small`, `standard_medium`, `standard_large`, `plus_small`, `plus_medium`, `plus_large`. See [pricing information](https://www.hashicorp.com/products/vault/pricing). Changing a cluster's size or tier is only available to admins. See [Scale a cluster](https://registry.terraform.io/providers/hashicorp/hcp/latest/docs/guides/vault-scaling).
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
+- `vault_plugin` (Block List) The external plugins to install on the vault cluster (see [below for nested schema](#nestedblock--vault_plugin))
### Read-Only
@@ -127,6 +128,15 @@ Optional:
- `delete` (String)
- `update` (String)
+
+
+### Nested Schema for `vault_plugin`
+
+Required:
+
+- `plugin_name` (String) The name of the plugin - Valid options for plugin name - 'venafi-pki-backend'
+- `plugin_type` (String) The type of the plugin - Valid options for plugin type - 'SECRET', 'AUTH', 'DATABASE'
+
-> **Note:** When establishing performance replication links between clusters in different HVNs, an HVN peering connection is required. This can be defined explicitly using an [`hcp_hvn_peering_connection`](hvn_peering_connection.md), or HCP will create the connection automatically (peering connections can be imported after creation using [terraform import](https://www.terraform.io/cli/import)). Note HVN peering [CIDR block requirements](https://cloud.hashicorp.com/docs/hcp/network/routes#cidr-block-requirements).
## Import
diff --git a/internal/clients/vault_cluster.go b/internal/clients/vault_cluster.go
index ea8c5444d..f8654d272 100644
--- a/internal/clients/vault_cluster.go
+++ b/internal/clients/vault_cluster.go
@@ -273,3 +273,92 @@ func DeleteVaultPathsFilter(ctx context.Context, client *Client, loc *sharedmode
return deleteResp.Payload, nil
}
+
+// AddPlugin will make a call to the Vault service to add a plugin to a Vault cluster
+func AddPlugin(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, clusterID string,
+ request *vaultmodels.HashicorpCloudVault20201125AddPluginRequest) (vaultmodels.HashicorpCloudVault20201125AddPluginResponse, error) {
+
+ region := &sharedmodels.HashicorpCloudLocationRegion{}
+ if loc.Region != nil {
+ region = loc.Region
+ }
+ locInternal := &vaultmodels.HashicorpCloudInternalLocationLocation{
+ OrganizationID: loc.OrganizationID,
+ ProjectID: loc.ProjectID,
+ Region: &vaultmodels.HashicorpCloudInternalLocationRegion{
+ Provider: region.Provider,
+ Region: region.Region,
+ },
+ }
+ request.Location = locInternal
+ request.ClusterID = clusterID
+ addPluginParams := vault_service.NewAddPluginParams()
+ addPluginParams.Context = ctx
+ addPluginParams.ClusterID = clusterID
+ addPluginParams.LocationProjectID = loc.ProjectID
+ addPluginParams.LocationOrganizationID = loc.OrganizationID
+ addPluginParams.Body = request
+
+ addPluginResp, err := client.Vault.AddPlugin(addPluginParams, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return addPluginResp.Payload, nil
+}
+
+// DeletePlugin will make a call to the Vault service to remove a plugin to a Vault cluster
+func DeletePlugin(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, clusterID string,
+ request *vaultmodels.HashicorpCloudVault20201125DeletePluginRequest) (vaultmodels.HashicorpCloudVault20201125DeletePluginResponse, error) {
+
+ region := &sharedmodels.HashicorpCloudLocationRegion{}
+ if loc.Region != nil {
+ region = loc.Region
+ }
+ locInternal := &vaultmodels.HashicorpCloudInternalLocationLocation{
+ OrganizationID: loc.OrganizationID,
+ ProjectID: loc.ProjectID,
+ Region: &vaultmodels.HashicorpCloudInternalLocationRegion{
+ Provider: region.Provider,
+ Region: region.Region,
+ },
+ }
+ request.Location = locInternal
+ request.ClusterID = clusterID
+ delPluginParams := vault_service.NewDeletePluginParams()
+ delPluginParams.Context = ctx
+ delPluginParams.ClusterID = clusterID
+ delPluginParams.LocationProjectID = loc.ProjectID
+ delPluginParams.LocationOrganizationID = loc.OrganizationID
+ delPluginParams.Body = request
+
+ delPluginResp, err := client.Vault.DeletePlugin(delPluginParams, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return delPluginResp.Payload, nil
+}
+
+// ListPlugins will make a call to the Vault service plugin status api to get all available plugins for the cluster.
+func ListPlugins(ctx context.Context, client *Client, loc *sharedmodels.HashicorpCloudLocationLocation, clusterID string) (*vaultmodels.HashicorpCloudVault20201125PluginRegistrationStatusResponse, error) {
+ region := &sharedmodels.HashicorpCloudLocationRegion{}
+ if loc.Region != nil {
+ region = loc.Region
+ }
+
+ listPluginsParams := vault_service.NewPluginRegistrationStatusParams()
+ listPluginsParams.Context = ctx
+ listPluginsParams.ClusterID = clusterID
+ listPluginsParams.LocationProjectID = loc.ProjectID
+ listPluginsParams.LocationOrganizationID = loc.OrganizationID
+ listPluginsParams.LocationRegionProvider = ®ion.Provider
+ listPluginsParams.LocationRegionRegion = ®ion.Region
+
+ listPluginsResp, err := client.Vault.PluginRegistrationStatus(listPluginsParams, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return listPluginsResp.Payload, nil
+}
diff --git a/internal/provider/data_source_vault_cluster.go b/internal/provider/data_source_vault_cluster.go
index 768a22b46..ae911fbb9 100644
--- a/internal/provider/data_source_vault_cluster.go
+++ b/internal/provider/data_source_vault_cluster.go
@@ -208,6 +208,26 @@ If a project is not configured in the HCP Provider config block, the oldest proj
},
},
},
+ "vault_plugin": {
+ Description: "The external plugins to install on the vault cluster",
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "plugin_name": {
+ Description: "The name of the plugin",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "plugin_type": {
+ Description: "The type of the plugin",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ },
+ },
},
}
}
@@ -242,8 +262,14 @@ func dataSourceVaultClusterRead(ctx context.Context, d *schema.ResourceData, met
d.SetId(url)
+ plugins, err := clients.ListPlugins(ctx, client, loc, clusterID)
+ if err != nil {
+ log.Printf("[ERROR] Vault cluster (%s) failed to list plugins", clusterID)
+ return diag.FromErr(err)
+ }
+
// Cluster found, update resource data.
- if err := setVaultClusterResourceData(d, cluster); err != nil {
+ if err := setVaultClusterResourceData(d, cluster, plugins.Plugins); err != nil {
return diag.FromErr(err)
}
diff --git a/internal/provider/resource_vault_cluster.go b/internal/provider/resource_vault_cluster.go
index 610aeb9fe..3ef09a69c 100644
--- a/internal/provider/resource_vault_cluster.go
+++ b/internal/provider/resource_vault_cluster.go
@@ -276,6 +276,37 @@ If a project is not configured in the HCP Provider config block, the oldest proj
},
},
},
+ // vault_plugin is a terraform resource used to specify a plugin for installation.
+ //
+ // plugin_name is only validated on updates because the PluginStatus API to list the valid available plugin names requires a created cluster.
+ // plugin_type is validated on create & update because there is a static list for valid plugint types.
+ "vault_plugin": {
+ Description: "The external plugins to install on the vault cluster",
+ Type: schema.TypeList,
+ Optional: true,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "plugin_name": {
+ Description: "The name of the plugin - Valid options for plugin name - 'venafi-pki-backend'",
+ Type: schema.TypeString,
+ Required: true,
+ DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool {
+ return strings.EqualFold(old, new)
+ },
+ },
+ "plugin_type": {
+ Description: "The type of the plugin - Valid options for plugin type - 'SECRET', 'AUTH', 'DATABASE'",
+ Type: schema.TypeString,
+ Required: true,
+ ValidateDiagFunc: validateVaultPluginType,
+ DiffSuppressFunc: func(_, old, new string, _ *schema.ResourceData) bool {
+ return strings.EqualFold(old, new)
+ },
+ },
+ },
+ },
+ },
"vault_public_endpoint_url": {
Description: "The public URL for the Vault cluster. This will be empty if `public_endpoint` is `false`.",
Type: schema.TypeString,
@@ -306,7 +337,6 @@ If a project is not configured in the HCP Provider config block, the oldest proj
}
func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
-
client := meta.(*clients.Client)
clusterID := d.Get("cluster_id").(string)
@@ -335,6 +365,11 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
return diagErr
}
+ pluginConfig, diagErr := getPluginConfig(d, nil)
+ if diagErr != nil {
+ return diagErr
+ }
+
// Use the hvn to get provider and region.
hvn, err := clients.GetHvnByID(ctx, client, loc, hvnID)
if err != nil {
@@ -506,7 +541,6 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
// If we pass the major version upgrade configuration we need to update it after the creation of the cluster,
// since the cluster is created by default to automatic upgrade
if mvuConfig != nil {
-
_, err := clients.UpdateVaultMajorVersionUpgradeConfig(ctx, client, clusterLocationShared, payload.ClusterID, mvuConfig)
if err != nil {
return diag.Errorf("error updating Vault cluster major version upgrade config (%s): %v", payload.ClusterID, err)
@@ -519,7 +553,21 @@ func resourceVaultClusterCreate(ctx context.Context, d *schema.ResourceData, met
}
}
- if err := setVaultClusterResourceData(d, cluster); err != nil {
+ // add plugins to cluster after cluster creation
+ for _, plugin := range pluginConfig {
+ _, err := clients.AddPlugin(ctx, client, clusterLocationShared, payload.ClusterID, plugin)
+ if err != nil {
+ return diag.Errorf("error adding plugin (%s) to Vault cluster (%s): %v", plugin.PluginName, payload.ClusterID, err)
+ }
+ }
+
+ plugins, err := clients.ListPlugins(ctx, client, loc, clusterID)
+ if err != nil {
+ log.Printf("[ERROR] Vault cluster (%s) failed to list plugins", clusterID)
+ return diag.FromErr(err)
+ }
+
+ if err := setVaultClusterResourceData(d, cluster, plugins.Plugins); err != nil {
return diag.FromErr(err)
}
@@ -557,8 +605,14 @@ func resourceVaultClusterRead(ctx context.Context, d *schema.ResourceData, meta
return nil
}
+ plugins, err := clients.ListPlugins(ctx, client, loc, clusterID)
+ if err != nil {
+ log.Printf("[ERROR] Vault cluster (%s) failed to list plugins", clusterID)
+ return diag.FromErr(err)
+ }
+
// Cluster found, update resource data.
- if err := setVaultClusterResourceData(d, cluster); err != nil {
+ if err := setVaultClusterResourceData(d, cluster, plugins.Plugins); err != nil {
return diag.FromErr(err)
}
@@ -610,6 +664,18 @@ func resourceVaultClusterUpdate(ctx context.Context, d *schema.ResourceData, met
return diagErr
}
+ // get list of plugins for plugin-name validation in getPluginConfig
+ plugins, err := clients.ListPlugins(ctx, client, loc, clusterID)
+ if err != nil {
+ log.Printf("[ERROR] Vault cluster (%s) failed to list plugins", clusterID)
+ return diag.FromErr(err)
+ }
+
+ newPluginConfig, diagErr := getPluginConfig(d, plugins.Plugins)
+ if diagErr != nil {
+ return diagErr
+ }
+
if d.HasChange("tier") || d.HasChange("metrics_config") || d.HasChange("audit_log_config") {
diagErr := updateVaultClusterConfig(ctx, client, d, cluster, clusterID)
if diagErr != nil {
@@ -680,7 +746,55 @@ func resourceVaultClusterUpdate(ctx context.Context, d *schema.ResourceData, met
return diag.Errorf("unable to retrieve Vault cluster (%s): %v", clusterID, err)
}
- if err := setVaultClusterResourceData(d, cluster); err != nil {
+ // on update, delete plugins that were removed from plugin config. Add all plugins in new config
+ if d.HasChange("vault_plugin") {
+ old, _ := d.GetChange("vault_plugin")
+
+ oldPlugins := old.([]interface{})
+
+ for _, oldP := range oldPlugins {
+ config, ok := oldP.(map[string]interface{})
+ if !ok {
+ return diag.Errorf("could not parse old plugin config: %v", err)
+ }
+
+ pluginName := config["plugin_name"].(string)
+ pluginType := config["plugin_type"].(string)
+
+ // if plugin in old config is not found in the new config, delete the plugin
+ found := false
+ for _, plugin := range newPluginConfig {
+ if strings.EqualFold(pluginName, plugin.PluginName) && strings.EqualFold(pluginType, plugin.PluginType) {
+ found = true
+ }
+ }
+
+ if !found {
+ req := &vaultmodels.HashicorpCloudVault20201125DeletePluginRequest{PluginName: pluginName, PluginType: pluginType}
+ _, err := clients.DeletePlugin(ctx, client, clusterLocationShared, clusterID, req)
+ if err != nil {
+ return diag.Errorf("error deleting plugin (%s) on Vault cluster (%s): %v", pluginName, clusterID, err)
+ }
+ }
+
+ }
+
+ // add all plugins in new plugin config
+ for _, plugin := range newPluginConfig {
+ _, err := clients.AddPlugin(ctx, client, clusterLocationShared, clusterID, plugin)
+ if err != nil {
+ return diag.Errorf("error adding plugin (%s) to Vault cluster (%s): %v", plugin.PluginName, clusterID, err)
+ }
+ }
+ }
+
+ plugins, err = clients.ListPlugins(ctx, client, loc, clusterID)
+ if err != nil {
+ log.Printf("[ERROR] Vault cluster (%s) failed to list plugins", clusterID)
+ return diag.FromErr(err)
+ }
+
+ if err := setVaultClusterResourceData(d, cluster, plugins.Plugins); err != nil {
return diag.FromErr(err)
}
@@ -807,8 +921,7 @@ func getClusterTier(d *schema.ResourceData) *string {
}
// setVaultClusterResourceData sets the KV pairs of the Vault cluster resource schema.
-func setVaultClusterResourceData(d *schema.ResourceData, cluster *vaultmodels.HashicorpCloudVault20201125Cluster) error {
-
+func setVaultClusterResourceData(d *schema.ResourceData, cluster *vaultmodels.HashicorpCloudVault20201125Cluster, plugins []*vaultmodels.HashicorpCloudVault20201125PluginRegistrationStatus) error {
if err := d.Set("cluster_id", cluster.ID); err != nil {
return err
}
@@ -937,6 +1050,20 @@ func setVaultClusterResourceData(d *schema.ResourceData, cluster *vaultmodels.Ha
}
}
+ var pluginConfig []map[string]any
+ for _, plugin := range plugins {
+ if plugin.IsRegistered {
+ pluginMap := map[string]any{}
+ pluginMap["plugin_name"] = plugin.PluginName
+ pluginMap["plugin_type"] = plugin.PluginType
+ pluginConfig = append(pluginConfig, pluginMap)
+ }
+ if err = d.Set("vault_plugin", pluginConfig); err != nil {
+ return err
+ }
+
+ }
+
return nil
}
@@ -1150,6 +1277,50 @@ func flattenMajorVersionUpgradeConfig(config *vaultmodels.HashicorpCloudVault202
return []interface{}{configMap}
}
+func getPluginConfig(d *schema.ResourceData, plugins []*vaultmodels.HashicorpCloudVault20201125PluginRegistrationStatus) ([]*vaultmodels.HashicorpCloudVault20201125AddPluginRequest, diag.Diagnostics) {
+ if !d.HasChange("vault_plugin") {
+ return nil, nil
+ }
+ configParam, ok := d.GetOk("vault_plugin")
+ if !ok {
+ return nil, nil
+ }
+
+ configIfaceArr, ok := configParam.([]interface{})
+ if !ok || len(configIfaceArr) == 0 {
+ return nil, nil
+ }
+
+ if !ok || len(configIfaceArr) == 0 {
+ return nil, nil
+ }
+
+ var pluginConfigs []*vaultmodels.HashicorpCloudVault20201125AddPluginRequest
+
+ for _, plugin := range configIfaceArr {
+ config, ok := plugin.(map[string]interface{})
+ if !ok {
+ return nil, nil
+ }
+ pluginName := config["plugin_name"].(string)
+ pluginType := config["plugin_type"].(string)
+
+ if plugins != nil {
+ err := validateVaultPluginName(pluginName, pluginType, plugins)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ pluginConfigs = append(pluginConfigs, &vaultmodels.HashicorpCloudVault20201125AddPluginRequest{
+ PluginName: pluginName,
+ PluginType: pluginType,
+ })
+ }
+
+ return pluginConfigs, nil
+}
+
func resourceVaultClusterImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
// with multi-projects, import arguments must become dynamic:
// use explicit project ID with terraform import:
diff --git a/internal/provider/resource_vault_cluster_const_test.go b/internal/provider/resource_vault_cluster_const_test.go
index b6dae1070..dcc4d9136 100644
--- a/internal/provider/resource_vault_cluster_const_test.go
+++ b/internal/provider/resource_vault_cluster_const_test.go
@@ -48,6 +48,10 @@ resource "hcp_vault_cluster" "test" {
major_version_upgrade_config {
upgrade_type = "MANUAL"
}
+ vault_plugin {
+ plugin_type = "SECRET"
+ plugin_name = "venafi-pki-backend"
+ }
}
`
diff --git a/internal/provider/resource_vault_cluster_test.go b/internal/provider/resource_vault_cluster_test.go
index d80ffd8b8..4e0826f57 100644
--- a/internal/provider/resource_vault_cluster_test.go
+++ b/internal/provider/resource_vault_cluster_test.go
@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+
"github.com/hashicorp/terraform-provider-hcp/internal/clients"
)
@@ -156,7 +157,7 @@ func testAccCheckVaultClusterDestroy(s *terraform.State) error {
func awsTestSteps(t *testing.T, inp inputT) []resource.TestStep {
in := &inp
return []resource.TestStep{
- createClusteAndTestAdminTokenGeneration(t, in),
+ createClusterAndTestAdminTokenGeneration(t, in),
importResourcesInTFState(t, in),
tfApply(t, in),
testTFDataSources(t, in),
@@ -169,7 +170,7 @@ func awsTestSteps(t *testing.T, inp inputT) []resource.TestStep {
func azureTestSteps(t *testing.T, inp inputT) []resource.TestStep {
in := &inp
return []resource.TestStep{
- createClusteAndTestAdminTokenGeneration(t, in),
+ createClusterAndTestAdminTokenGeneration(t, in),
importResourcesInTFState(t, in),
tfApply(t, in),
testTFDataSources(t, in),
@@ -178,7 +179,7 @@ func azureTestSteps(t *testing.T, inp inputT) []resource.TestStep {
}
// This step tests Vault cluster and admin token resource creation.
-func createClusteAndTestAdminTokenGeneration(t *testing.T, in *inputT) resource.TestStep {
+func createClusterAndTestAdminTokenGeneration(t *testing.T, in *inputT) resource.TestStep {
return resource.TestStep{
Config: testConfig(in.tf),
Check: resource.ComposeTestCheckFunc(
@@ -285,11 +286,13 @@ func updateClusterTier(t *testing.T, in *inputT) resource.TestStep {
resource.TestCheckResourceAttr(vaultClusterResourceName, "major_version_upgrade_config.0.upgrade_type", "SCHEDULED"),
resource.TestCheckResourceAttr(vaultClusterResourceName, "major_version_upgrade_config.0.maintenance_window_day", "WEDNESDAY"),
resource.TestCheckResourceAttr(vaultClusterResourceName, "major_version_upgrade_config.0.maintenance_window_time", "WINDOW_12AM_4AM"),
+ resource.TestCheckNoResourceAttr(in.VaultClusterResourceName, "vault_plugin.0"),
+ resource.TestCheckNoResourceAttr(in.VaultClusterResourceName, "vault_plugin.0"),
),
}
}
-// This step verifies the successful update of "public_endpoint", "audit_log", "metrics" and MVU config
+// This step verifies the successful update of "public_endpoint", "audit_log", "metrics", MVU config, and plugin config
func updateVaultPublicEndpointObservabilityDataAndMVU(t *testing.T, in *inputT) resource.TestStep {
newIn := *in
newIn.PublicEndpoint = "true"
@@ -307,6 +310,8 @@ func updateVaultPublicEndpointObservabilityDataAndMVU(t *testing.T, in *inputT)
resource.TestCheckResourceAttrSet(in.VaultClusterResourceName, "audit_log_config.0.datadog_api_key"),
resource.TestCheckResourceAttr(in.VaultClusterResourceName, "audit_log_config.0.datadog_region", "us1"),
resource.TestCheckResourceAttr(in.VaultClusterResourceName, "major_version_upgrade_config.0.upgrade_type", "MANUAL"),
+ resource.TestCheckResourceAttr(in.VaultClusterResourceName, "vault_plugin.0.plugin_name", "venafi-pki-backend"),
+ resource.TestCheckResourceAttr(in.VaultClusterResourceName, "vault_plugin.0.plugin_type", "SECRET"),
),
}
}
@@ -330,6 +335,8 @@ func updateTierPublicEndpointAndRemoveObservabilityData(t *testing.T, in *inputT
resource.TestCheckResourceAttr(in.VaultClusterResourceName, "major_version_upgrade_config.0.upgrade_type", "SCHEDULED"),
resource.TestCheckResourceAttr(in.VaultClusterResourceName, "major_version_upgrade_config.0.maintenance_window_day", "WEDNESDAY"),
resource.TestCheckResourceAttr(in.VaultClusterResourceName, "major_version_upgrade_config.0.maintenance_window_time", "WINDOW_12AM_4AM"),
+ resource.TestCheckNoResourceAttr(in.VaultClusterResourceName, "vault_plugin.0"),
+ resource.TestCheckNoResourceAttr(in.VaultClusterResourceName, "vault_plugin.0"),
),
}
}
diff --git a/internal/provider/validators.go b/internal/provider/validators.go
index cf05e8041..bc60c4b70 100644
--- a/internal/provider/validators.go
+++ b/internal/provider/validators.go
@@ -441,3 +441,44 @@ func validateBoundaryPassword(v interface{}, path cty.Path) diag.Diagnostics {
return diagnostics
}
+
+func validateVaultPluginType(v interface{}, path cty.Path) diag.Diagnostics {
+ var diagnostics diag.Diagnostics
+
+ err := vaultmodels.HashicorpCloudVault20201125PluginType(strings.ToUpper(v.(string))).Validate(strfmt.Default)
+ if err != nil {
+ enumList := regexp.MustCompile(`\[.*\]`).FindString(err.Error())
+ expectedEnumList := strings.ToLower(enumList)
+ // Remove invalid option from allowed list in error message
+ expectedEnumList = strings.ReplaceAll(
+ expectedEnumList,
+ strings.ToLower(string(vaultmodels.HashicorpCloudVault20201125PluginTypePLUGINTYPEINVALID)),
+ "",
+ )
+ msg := fmt.Sprintf("expected '%v' to be one of: %v", v, expectedEnumList)
+ diagnostics = append(diagnostics, diag.Diagnostic{
+ Severity: diag.Error,
+ Summary: msg,
+ Detail: msg + " (value is case-insensitive).",
+ AttributePath: path,
+ })
+ }
+
+ return diagnostics
+}
+
+func validateVaultPluginName(pluginName string, pluginType string, plugins []*vaultmodels.HashicorpCloudVault20201125PluginRegistrationStatus) diag.Diagnostics {
+ var found bool
+ for _, plugin := range plugins {
+ if strings.EqualFold(pluginName, plugin.PluginName) && strings.EqualFold(pluginType, string(*plugin.PluginType)) {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ return diag.Errorf(fmt.Sprintf("plugin of plugin name: %s and plugin type: %s is not supported for installation by HCP Vault", pluginName, pluginType))
+ }
+
+ return nil
+}