From ca87be3fd76212220f7f1ebe6353dbaedc049f64 Mon Sep 17 00:00:00 2001 From: Mohammad AMLA Date: Sat, 20 Sep 2025 22:41:56 +0200 Subject: [PATCH] feat: add mounts datasource feat: add mounts datasource feat: add mounts datasource --- vault/data_source_mounts.go | 111 +++++++++++++++++++++++++++++++ vault/data_source_mounts_test.go | 82 +++++++++++++++++++++++ vault/provider.go | 4 ++ website/docs/d/mounts.html.md | 50 ++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 vault/data_source_mounts.go create mode 100644 vault/data_source_mounts_test.go create mode 100644 website/docs/d/mounts.html.md diff --git a/vault/data_source_mounts.go b/vault/data_source_mounts.go new file mode 100644 index 000000000..3a56265a6 --- /dev/null +++ b/vault/data_source_mounts.go @@ -0,0 +1,111 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "context" + "sort" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-vault/internal/provider" +) + +func mountsDataSource() *schema.Resource { + return &schema.Resource{ + ReadContext: provider.ReadContextWrapper(mountsDataSourceRead), + Schema: map[string]*schema.Schema{ + "mounts": { + Type: schema.TypeList, + Computed: true, + Description: "Liste des mounts activés dans Vault", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "accessor": { + Type: schema.TypeString, + Computed: true, + Description: "Internal accessor for the mount.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "Human-readable description of the mount.", + }, + "local": { + Type: schema.TypeBool, + Computed: true, + Description: "True if the mount is local to this Vault node.", + }, + "options": { + Type: schema.TypeMap, + Computed: true, + Description: "Key-value options that configure the mount.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "path": { + Type: schema.TypeString, + Computed: true, + Description: "Full path of the mount, ending with a slash (/).", + }, + "seal_wrap": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates whether seal wrapping is enabled for this mount.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Backend type of the mount (e.g., kv, pki).", + }, + "uuid": { + Type: schema.TypeString, + Computed: true, + Description: "Unique identifier (UUID) of the mount.", + }, + }, + }, + }, + }, + } +} + +func mountsDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client, err := provider.GetClient(d, meta) + if err != nil { + return diag.FromErr(err) + } + mounts, err := client.Sys().ListMountsWithContext(ctx) + if err != nil { + return diag.FromErr(err) + } + + // Extraire et trier les paths pour un ordre stable + paths := make([]string, 0, len(mounts)) + for path := range mounts { + paths = append(paths, path) + } + sort.Strings(paths) + + result := make([]map[string]any, 0, len(paths)) + for _, path := range paths { + m := mounts[path] + result = append(result, map[string]any{ + "accessor": m.Accessor, + "description": m.Description, + "local": m.Local, + "options": m.Options, + "path": path, + "seal_wrap": m.SealWrap, + "type": m.Type, + "uuid": m.UUID, + }) + } + + if err := d.Set("mounts", result); err != nil { + return diag.FromErr(err) + } + + d.SetId("vault-mounts") + return nil +} diff --git a/vault/data_source_mounts_test.go b/vault/data_source_mounts_test.go new file mode 100644 index 000000000..62d53fa88 --- /dev/null +++ b/vault/data_source_mounts_test.go @@ -0,0 +1,82 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package vault + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/hashicorp/terraform-provider-vault/testutil" +) + +func TestAccDataSourceMounts(t *testing.T) { + kvPath := acctest.RandomWithPrefix("tf-test-kv-backend") + pkiPath := acctest.RandomWithPrefix("tf-test-pki-backend") + dataName := "data.vault_mounts.test" + resource.Test(t, resource.TestCase{ + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories(context.Background(), t), + PreCheck: func() { + testutil.TestAccPreCheck(t) + }, + Steps: []resource.TestStep{ + { + Config: testMountsDataSource(kvPath, pkiPath), + Check: resource.ComposeTestCheckFunc( + testCheckMountInList(dataName, kvPath+"/", "kv"), + testCheckMountInList(dataName, pkiPath+"/", "pki"), + ), + }, + }, + }) +} + +func testMountsDataSource(kvPath, pkiPath string) string { + return fmt.Sprintf(` +resource "vault_mount" "kv" { + path = "%s" + type = "kv" + description = "KV secret engine mount" +} + +resource "vault_mount" "pki" { + path = "%s" + type = "pki" + description = "PKI secret engine mount" +} + + +data "vault_mounts" "test" { + depends_on = [ vault_mount.kv,vault_mount.pki ] +}`, kvPath, pkiPath) +} + +func testCheckMountInList(name, expectedPath, expectedType string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("resource %s not found", name) + } + + countStr := rs.Primary.Attributes["mounts.#"] + count, err := strconv.Atoi(countStr) + if err != nil { + return err + } + + for i := range count { + path := rs.Primary.Attributes[fmt.Sprintf("mounts.%d.path", i)] + typ := rs.Primary.Attributes[fmt.Sprintf("mounts.%d.type", i)] + if path == expectedPath && typ == expectedType { + return nil + } + } + return fmt.Errorf("mount with path %s and type %s not found", expectedPath, expectedType) + } +} diff --git a/vault/provider.go b/vault/provider.go index 364f8b4cf..c2e884b37 100644 --- a/vault/provider.go +++ b/vault/provider.go @@ -223,6 +223,10 @@ var ( Resource: UpdateSchemaResource(transitCMACDataSource()), PathInventory: []string{"/transit/cmac/{name}/{url_mac_length}"}, }, + "vault_mounts": { + Resource: UpdateSchemaResource(mountsDataSource()), + PathInventory: []string{"/sys/mounts"}, + }, } ResourceRegistry = map[string]*provider.Description{ diff --git a/website/docs/d/mounts.html.md b/website/docs/d/mounts.html.md new file mode 100644 index 000000000..059b4acd0 --- /dev/null +++ b/website/docs/d/mounts.html.md @@ -0,0 +1,50 @@ +--- +layout: "vault" +page_title: "Vault: vault_mounts data source" +sidebar_current: "docs-vault-datasource-mounts" +description: |- + Lists all mounts. +--- + +# vault_mounts + +Lists all issuers under a particular mount. + +~> **Important** All data retrieved from Vault will be +written in cleartext to state file generated by Terraform, will appear in +the console output when Terraform runs, and may be included in plan files +if secrets are interpolated into any resource attributes. +Protect these artifacts accordingly. See +[the main provider documentation](../index.html) +for more details. + +## Example Usage + +```hcl +data "vault_mounts" "all" {} +``` + +## Argument Reference + +The vault_mounts data source does not require any arguments. Optionally, the namespace argument can be used to target Vault Enterprise namespaces: + +- `namespace` - (Optional) The namespace of the target resource. + The value should not contain leading or trailing forward slashes. + The `namespace` is always relative to the provider's configured [namespace](/docs/providers/vault/index.html#namespace). + _Available only for Vault Enterprise_. + +## Attributes Reference + +The following attributes are exported by the data source: + +| Attribute | Type | Description | +| ------------- | ------------ | ----------------------------------------------------------------------------- | +| `mounts` | list(object) | A list of objects representing each enabled mount. Each object contains: | +| `path` | string | The full path of the mount, ending with `/`. | +| `type` | string | The type of the backend (e.g., `kv`, `pki`). | +| `description` | string | Human-readable description of the mount. | +| `options` | map(string) | Key-value map of mount options (all values are strings). | +| `accessor` | string | Internal Vault accessor for the mount. | +| `local` | bool | Boolean indicating if the mount is local to the Vault node. | +| `seal_wrap` | bool | Boolean indicating if seal wrapping is enabled. | +| `uuid` | string | Unique UUID of the mount. |