Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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) ID of the group, if the scope is GROUP
- `project_id` (String) 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 gitlab-proj-sync
```
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 gitlab-proj-sync
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
34 changes: 32 additions & 2 deletions internal/provider/vaultsecrets/resource_vault_secrets_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ 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"
Expand All @@ -30,6 +32,9 @@ 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"`

syncNames []string `tfsdk:"-"`
}

var _ resource.Resource = &resourceVaultSecretsApp{}
Expand Down Expand Up @@ -90,7 +95,16 @@ 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,
Validators: []validator.Set{
setvalidator.ValueStringsAre(
slugValidator,
),
},
}},
}
}

Expand Down Expand Up @@ -124,6 +138,7 @@ func (r *resourceVaultSecretsApp) Create(ctx context.Context, req resource.Creat
Body: &secretmodels.SecretServiceCreateAppBody{
Name: app.AppName.ValueString(),
Description: app.Description.ValueString(),
SyncNames: app.syncNames,
},
OrganizationID: app.OrganizationID.ValueString(),
ProjectID: app.ProjectID.ValueString(),
Expand Down Expand Up @@ -170,6 +185,7 @@ func (r *resourceVaultSecretsApp) Update(ctx context.Context, req resource.Updat
response, err := r.client.VaultSecrets.UpdateApp(&secret_service.UpdateAppParams{
Body: &secretmodels.SecretServiceUpdateAppBody{
Description: app.Description.ValueString(),
SyncNames: app.syncNames,
},
Name: app.AppName.ValueString(),
OrganizationID: app.OrganizationID.ValueString(),
Expand Down Expand Up @@ -216,9 +232,11 @@ 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)
a.syncNames = make([]string, 0, len(a.SyncNames.Elements()))
a.SyncNames.ElementsAs(ctx, &a.syncNames, false)

return diag.Diagnostics{}
}
Expand All @@ -237,5 +255,17 @@ 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))
}

if len(syncs) > 0 {
a.SyncNames, diags = types.SetValue(types.StringType, syncs)
if diags.HasError() {
return diags
}
}

return diags
}
78 changes: 64 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,16 @@ 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"
projSyncName = generateRandomSlug()
groupSyncName = generateRandomSlug()
gitLabToken = checkRequiredEnvVarOrFail(t, "GITLAB_ACCESS_TOKEN")
)

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
Expand All @@ -30,21 +35,58 @@ 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_proj_sync" {
name = %q
integration_name = hcp_vault_secrets_integration.acc_test.name
gitlab_config = {
scope = "PROJECT"
project_id = "123456789"
}
}
resource "hcp_vault_secrets_sync" "gitlab_group_sync" {
name = %q
integration_name = hcp_vault_secrets_integration.acc_test.name
gitlab_config = {
scope = "GROUP"
project_id = "987654321"
}
}
resource "hcp_vault_secrets_app" "acc_test_app" {
app_name = %q
description = %q
sync_names = [hcp_vault_secrets_sync.gitlab_sync.name]
}
`, integrationName1, gitLabToken, projSyncName, groupSyncName, appName2, description2),
Check: resource.ComposeTestCheckFunc(
appCheckFunc(appName2, description2, []string{projSyncName, groupSyncName})...,
),
},
// Deleting the app out of band causes a recreation
Expand All @@ -63,7 +105,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 +129,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 +153,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,11 +291,14 @@ 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,
},
},
Validators: []validator.Object{
exactlyOneIntegrationTypeFieldsValidator,
},
},
}

Expand Down
Loading