Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
7 changes: 7 additions & 0 deletions .changelog/1196.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:feature
Add support for sync resource in HCP Vault Secrets
```

```release-note:improvement
Associate a sync with an HCP Vault Secrets App
```
1 change: 1 addition & 0 deletions docs/resources/vault_secrets_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ resource "hcp_vault_secrets_app" "example" {

- `description` (String) The Vault Secrets app description
- `project_id` (String) The ID of the HCP project where the HCP Vault Secrets app is located.
- `sync_names` (Set of String) Set of sync names to associate with this app.

### Read-Only

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/vault_secrets_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ Read-Only:

Required:

- `token` (String, Sensitive) Access token used to authenticate against the target GitLab account.
- `token` (String, Sensitive) Access token used to authenticate against the target GitLab account. This token must have privilege to create CI/CD variables.


<a id="nestedatt--mongodb_atlas_static_credentials"></a>
Expand Down
59 changes: 59 additions & 0 deletions docs/resources/vault_secrets_sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
page_title: "Resource hcp_vault_secrets_sync"
subcategory: "HCP Vault Secrets"
description: |-
The Vault Secrets sync resource manages an integration.
---

# hcp_vault_secrets_sync (Resource)

The Vault Secrets sync resource manages an integration.

## Example Usage

```terraform
# the provider is derived from the integration name
resource "hcp_vault_secrets_sync" "example_aws_sync" {
name = "my-aws-1"
integration_name = "my-integration-1"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `integration_name` (String) The Vault Secrets integration name.
- `name` (String) The Vault Secrets Sync name.

### Optional

- `gitlab_config` (Attributes) Configuration parameters used to determine the sync destination. (see [below for nested schema](#nestedatt--gitlab_config))
- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted.

### Read-Only

- `id` (String) Required ID field that is set to the sync name.
- `organization_id` (String) HCP organization ID that owns the HCP Vault Secrets integration.

<a id="nestedatt--gitlab_config"></a>
### Nested Schema for `gitlab_config`

Required:

- `scope` (String) The scope to which values apply. The valid options are GROUP and PROJECT

Optional:

- `group_id` (String, Sensitive) ID of the group, if the scope is GROUP
- `project_id` (String, Sensitive) ID of the project, if the scope is PROJECT

## Import

Import is supported using the following syntax:

```shell
# Vault Secrets Integration can be imported by specifying the name of the integration
terraform import hcp_vault_secrets_sync.example my-sync-name
```
2 changes: 2 additions & 0 deletions examples/resources/hcp_vault_secrets_sync/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Vault Secrets Integration can be imported by specifying the name of the integration
terraform import hcp_vault_secrets_sync.example my-sync-name
5 changes: 5 additions & 0 deletions examples/resources/hcp_vault_secrets_sync/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# the provider is derived from the integration name
resource "hcp_vault_secrets_sync" "example_aws_sync" {
name = "my-aws-1"
integration_name = "my-integration-1"
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res
vaultsecrets.NewVaultSecretsIntegrationResource,
vaultsecrets.NewVaultSecretsDynamicSecretResource,
vaultsecrets.NewVaultSecretsRotatingSecretResource,
vaultsecrets.NewVaultSecretsSyncResource,
// Vault Secrets Deprecated
vaultsecrets.NewVaultSecretsIntegrationAWSResource,
vaultsecrets.NewVaultSecretsIntegrationAzureResource,
Expand Down
32 changes: 30 additions & 2 deletions internal/provider/vaultsecrets/resource_vault_secrets_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import (

"github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/client/secret_service"
secretmodels "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-secrets/stable/2023-11-28/models"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"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/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
Expand All @@ -30,6 +33,7 @@ type App struct {
ProjectID types.String `tfsdk:"project_id"`
OrganizationID types.String `tfsdk:"organization_id"`
ResourceName types.String `tfsdk:"resource_name"`
SyncNames types.Set `tfsdk:"sync_names"`
}

var _ resource.Resource = &resourceVaultSecretsApp{}
Expand Down Expand Up @@ -90,7 +94,19 @@ func (r *resourceVaultSecretsApp) Schema(_ context.Context, _ resource.SchemaReq
Computed: true,
Description: "The app's resource name in the format secrets/project/<project ID>/app/<app Name>.",
},
},
"sync_names": schema.SetAttribute{
Description: "Set of sync names to associate with this app.",
Optional: true,
ElementType: types.StringType,
PlanModifiers: []planmodifier.Set{
setplanmodifier.RequiresReplace(),
},
Validators: []validator.Set{
setvalidator.ValueStringsAre(
stringvalidator.LengthBetween(1, 511),
),
},
}},
}
}

Expand Down Expand Up @@ -120,10 +136,13 @@ func (r *resourceVaultSecretsApp) Create(ctx context.Context, req resource.Creat
return nil, fmt.Errorf("invalid resource type, expected *App, got: %T, this is a bug on the provider", i)
}

syncNames := make([]string, len(app.SyncNames.Elements()))

response, err := r.client.VaultSecrets.CreateApp(&secret_service.CreateAppParams{
Body: &secretmodels.SecretServiceCreateAppBody{
Name: app.AppName.ValueString(),
Description: app.Description.ValueString(),
SyncNames: syncNames,
},
OrganizationID: app.OrganizationID.ValueString(),
ProjectID: app.ProjectID.ValueString(),
Expand Down Expand Up @@ -216,7 +235,7 @@ func (a *App) projectID() types.String {
return a.ProjectID
}

func (a *App) initModel(_ context.Context, orgID, projID string) diag.Diagnostics {
func (a *App) initModel(ctx context.Context, orgID, projID string) diag.Diagnostics {
a.OrganizationID = types.StringValue(orgID)
a.ProjectID = types.StringValue(projID)

Expand All @@ -237,5 +256,14 @@ func (a *App) fromModel(_ context.Context, orgID, projID string, model any) diag
a.ID = types.StringValue(appModel.ResourceID)
a.ResourceName = types.StringValue(appModel.ResourceName)

var syncs []attr.Value
for _, c := range appModel.SyncNames {
syncs = append(syncs, types.StringValue(c))
}
a.SyncNames, diags = types.SetValue(types.StringType, syncs)
if diags.HasError() {
return diags
}

return diags
}
69 changes: 55 additions & 14 deletions internal/provider/vaultsecrets/resource_vault_secrets_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ import (
)

func TestAccVaultSecretsResourceApp(t *testing.T) {
appName1 := generateRandomSlug()
appName2 := generateRandomSlug()

description1 := "my description 1"
description2 := "my description 2"
var (
integrationName1 = generateRandomSlug()
appName1 = generateRandomSlug()
appName2 = generateRandomSlug()
description1 = "my description 1"
description2 = "my description 2"
syncName = generateRandomSlug()
gitLabToken = checkRequiredEnvVarOrFail(t, "GITLAB_ACCESS_TOKEN")
)

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
Expand All @@ -30,21 +34,50 @@ func TestAccVaultSecretsResourceApp(t *testing.T) {
{
Config: appConfig(appName1, description1),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName1, description1)...,
appCheckFunc(appName1, description1, nil)...,
),
},
// Changing an immutable field causes a recreation
{
Config: appConfig(appName2, description1),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description1)...,
appCheckFunc(appName2, description1, nil)...,
),
},
// Changing mutable fields causes an update
{
Config: appConfig(appName2, description2),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description2)...,
appCheckFunc(appName2, description2, nil)...,
),
},
// Changing the sync_names causes an update
{
Config: fmt.Sprintf(`
resource "hcp_vault_secrets_integration" "acc_test" {
name = %q
capabilities = ["SYNC"]
provider_type = "gitlab"
gitlab_access = {
token = %q
}
}
resource "hcp_vault_secrets_sync" "gitlab_sync" {
name = %q
integration_name = hcp_vault_secrets_integration.acc_test.name
gitlab_config = {
scope = "PROJECT"
project_id = "1234"
}
}
resource "hcp_vault_secrets_app" "acc_test_app" {
app_name = %q
description = %q
sync_names = [hcp_vault_secrets_sync.gitlab_sync.name]
}
`, integrationName1, gitLabToken, syncName, appName2, description2),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description2, []string{syncName})...,
),
},
// Deleting the app out of band causes a recreation
Expand All @@ -63,7 +96,7 @@ func TestAccVaultSecretsResourceApp(t *testing.T) {
},
Config: appConfig(appName2, description2),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description2)...,
appCheckFunc(appName2, description2, nil)...,
),
PlanOnly: true,
ExpectNonEmptyPlan: true,
Expand All @@ -87,7 +120,7 @@ func TestAccVaultSecretsResourceApp(t *testing.T) {
},
Config: appConfig(appName2, description2),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description2)...,
appCheckFunc(appName2, description2, nil)...,
),
ResourceName: "hcp_vault_secrets_app.acc_test_app",
ImportStateId: appName2,
Expand All @@ -111,18 +144,26 @@ func appConfig(appName, description string) string {
resource "hcp_vault_secrets_app" "acc_test_app" {
app_name = %q
description = %q
sync_names = []
}`, appName, description)
}

func appCheckFunc(appName, description string) []resource.TestCheckFunc {
return []resource.TestCheckFunc{
func appCheckFunc(appName, description string, syncNames []string) []resource.TestCheckFunc {
syncsTestFns := make([]resource.TestCheckFunc, 0, len(syncNames))
for _, syncName := range syncNames {
syncsTestFns = append(syncsTestFns, resource.TestCheckTypeSetElemAttr("hcp_vault_secrets_app.acc_test_app", "sync_names.*", syncName))
}

testFns := []resource.TestCheckFunc{
resource.TestCheckResourceAttrSet("hcp_vault_secrets_app.acc_test_app", "organization_id"),
resource.TestCheckResourceAttrSet("hcp_vault_secrets_app.acc_test_app", "id"),
resource.TestCheckResourceAttrSet("hcp_vault_secrets_app.acc_test_app", "resource_name"),
resource.TestCheckResourceAttr("hcp_vault_secrets_app.acc_test_app", "project_id", os.Getenv("HCP_PROJECT_ID")),
resource.TestCheckResourceAttr("hcp_vault_secrets_app.acc_test_app", "app_name", appName),
resource.TestCheckResourceAttr("hcp_vault_secrets_app.acc_test_app", "description", description),
}
resource.TestCheckResourceAttr("hcp_vault_secrets_app.acc_test_app", "description", description)}

testFns = append(testFns, syncsTestFns...)
return testFns
}

func appExists(t *testing.T, name string) bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func (r *resourceVaultSecretsIntegration) Schema(_ context.Context, _ resource.S
Optional: true,
Attributes: map[string]schema.Attribute{
"token": schema.StringAttribute{
Description: "Access token used to authenticate against the target GitLab account.",
Description: "Access token used to authenticate against the target GitLab account. This token must have privilege to create CI/CD variables.",
Required: true,
Sensitive: true,
},
Expand Down
Loading