Skip to content

Commit

Permalink
Dynamic secret resource implementation (#1097)
Browse files Browse the repository at this point in the history
* * dynamic secret resource implementation
  • Loading branch information
maxcoulombe authored Sep 24, 2024
1 parent 2119061 commit 34b9c15
Show file tree
Hide file tree
Showing 25 changed files with 1,790 additions and 37 deletions.
3 changes: 3 additions & 0 deletions .changelog/1097.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
add vault_secrets_dynamic_secret resource
```
3 changes: 3 additions & 0 deletions .changelog/1101.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
add vault_secrets_rotating_secret resource
```
73 changes: 73 additions & 0 deletions docs/resources/vault_secrets_dynamic_secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "hcp_vault_secrets_dynamic_secret Resource - terraform-provider-hcp"
subcategory: ""
description: |-
The Vault Secrets dynamic secret resource manages a dynamic secret configuration.
---

# hcp_vault_secrets_dynamic_secret (Resource)

The Vault Secrets dynamic secret resource manages a dynamic secret configuration.

## Example Usage

```terraform
resource "hcp_vault_secrets_dynamic_secret" "example_aws" {
app_name = "my-app-1"
secret_provider = "aws"
name = "my_aws_1"
integration_name = "my-integration-1"
default_ttl = "900s"
aws_assume_role = {
iam_role_arn = "arn:aws:iam::<account_id>:role/<role_name>"
}
}
resource "hcp_vault_secrets_dynamic_secret" "example_gcp" {
app_name = "my-app-1"
secret_provider = "gcp"
name = "my_gcp_1"
integration_name = "my-integration-1"
default_ttl = "900s"
gcp_impersonate_service_account = {
service_account_email = "<name>@<project>.iam.gserviceaccount.com"
}
}
```

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

### Required

- `app_name` (String) Vault Secrets application name that owns the secret.
- `integration_name` (String) The Vault Secrets integration name with the capability to manage the secret's lifecycle.
- `name` (String) The Vault Secrets secret name.
- `secret_provider` (String) The third party platform the dynamic credentials give access to. One of `aws` or `gcp`.

### Optional

- `aws_assume_role` (Attributes) AWS configuration to generate dynamic credentials by assuming an IAM role. Required if `secret_provider` is `aws`. (see [below for nested schema](#nestedatt--aws_assume_role))
- `default_ttl` (String) TTL the generated credentials will be valid for.
- `gcp_impersonate_service_account` (Attributes) GCP configuration to generate dynamic credentials by impersonating a service account. Required if `secret_provider` is `gcp`. (see [below for nested schema](#nestedatt--gcp_impersonate_service_account))
- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted.

### Read-Only

- `organization_id` (String) HCP organization ID that owns the HCP Vault Secrets integration.

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

Required:

- `iam_role_arn` (String) AWS IAM role ARN to assume when generating credentials.


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

Required:

- `service_account_email` (String) GCP service account email to impersonate.
111 changes: 111 additions & 0 deletions docs/resources/vault_secrets_rotating_secret.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "hcp_vault_secrets_rotating_secret Resource - terraform-provider-hcp"
subcategory: ""
description: |-
The Vault Secrets rotating secret resource manages a rotating secret configuration.
---

# hcp_vault_secrets_rotating_secret (Resource)

The Vault Secrets rotating secret resource manages a rotating secret configuration.

## Example Usage

```terraform
resource "hcp_vault_secrets_rotating_secret" "example_aws" {
app_name = "my-app-1"
secret_provider = "aws"
name = "my_aws_1"
integration_name = "my-aws-1"
rotation_policy_name = "built-in:60-days-2-active"
aws_access_keys = {
iam_username = "my-iam-username"
}
}
resource "hcp_vault_secrets_rotating_secret" "example_gcp" {
app_name = "my-app-1"
secret_provider = "gcp"
name = "my_gcp_1"
integration_name = "my-gcp-1"
rotation_policy_name = "built-in:60-days-2-active"
gcp_service_account_key = {
service_account_email = "<name>>@<project>.iam.gserviceaccount.com"
}
}
resource "hcp_vault_secrets_rotating_secret" "example_mongodb_atlas" {
app_name = "my-app-1"
secret_provider = "mongodb_atlas"
name = "my_mongodb_atlas_1"
integration_name = "my-mongodbatlas-1"
rotation_policy_name = "built-in:60-days-2-active"
mongodb_atlas_user = {
project_id = "<uuid>>"
database_name = "my-cluster-0"
roles = ["readWrite", "read"]
}
}
resource "hcp_vault_secrets_rotating_secret" "example_twilio" {
app_name = "my-app-1"
secret_provider = "twilio"
name = "my_twilio_1"
integration_name = "my-twilio-1"
rotation_policy_name = "built-in:60-days-2-active"
twilio_api_key = {}
}
```

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

### Required

- `app_name` (String) Vault Secrets application name that owns the secret.
- `integration_name` (String) The Vault Secrets integration name with the capability to manage the secret's lifecycle.
- `name` (String) The Vault Secrets secret name.
- `rotation_policy_name` (String) Name of the rotation policy that governs the rotation of the secret.
- `secret_provider` (String) The third party platform the dynamic credentials give access to. One of `aws` or `gcp`.

### Optional

- `aws_access_keys` (Attributes) AWS configuration to manage the access key rotation for the given IAM user. Required if `secret_provider` is `aws`. (see [below for nested schema](#nestedatt--aws_access_keys))
- `gcp_service_account_key` (Attributes) GCP configuration to manage the service account key rotation for the given service account. Required if `secret_provider` is `gcp`. (see [below for nested schema](#nestedatt--gcp_service_account_key))
- `mongodb_atlas_user` (Attributes) MongoDB Atlas configuration to manage the user password rotation on the given database. Required if `secret_provider` is `mongodb_atlas`. (see [below for nested schema](#nestedatt--mongodb_atlas_user))
- `project_id` (String) HCP project ID that owns the HCP Vault Secrets integration. Inferred from the provider configuration if omitted.
- `twilio_api_key` (Attributes) Twilio configuration to manage the api key rotation on the given account. Required if `secret_provider` is `twilio`. (see [below for nested schema](#nestedatt--twilio_api_key))

### Read-Only

- `organization_id` (String) HCP organization ID that owns the HCP Vault Secrets integration.

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

Required:

- `iam_username` (String) AWS IAM username to rotate the access keys for.


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

Required:

- `service_account_email` (String) GCP service account email to impersonate.


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

Required:

- `database_name` (String) MongoDB Atlas database or cluster name to rotate the username and password for.
- `project_id` (String) MongoDB Atlas project ID to rotate the username and password for.
- `roles` (List of String) MongoDB Atlas roles to assign to the rotating user.


<a id="nestedatt--twilio_api_key"></a>
### Nested Schema for `twilio_api_key`
21 changes: 21 additions & 0 deletions examples/resources/hcp_vault_secrets_dynamic_secret/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "hcp_vault_secrets_dynamic_secret" "example_aws" {
app_name = "my-app-1"
secret_provider = "aws"
name = "my_aws_1"
integration_name = "my-integration-1"
default_ttl = "900s"
aws_assume_role = {
iam_role_arn = "arn:aws:iam::<account_id>:role/<role_name>"
}
}

resource "hcp_vault_secrets_dynamic_secret" "example_gcp" {
app_name = "my-app-1"
secret_provider = "gcp"
name = "my_gcp_1"
integration_name = "my-integration-1"
default_ttl = "900s"
gcp_impersonate_service_account = {
service_account_email = "<name>@<project>.iam.gserviceaccount.com"
}
}
43 changes: 43 additions & 0 deletions examples/resources/hcp_vault_secrets_rotating_secret/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
resource "hcp_vault_secrets_rotating_secret" "example_aws" {
app_name = "my-app-1"
secret_provider = "aws"
name = "my_aws_1"
integration_name = "my-aws-1"
rotation_policy_name = "built-in:60-days-2-active"
aws_access_keys = {
iam_username = "my-iam-username"
}
}

resource "hcp_vault_secrets_rotating_secret" "example_gcp" {
app_name = "my-app-1"
secret_provider = "gcp"
name = "my_gcp_1"
integration_name = "my-gcp-1"
rotation_policy_name = "built-in:60-days-2-active"
gcp_service_account_key = {
service_account_email = "<name>>@<project>.iam.gserviceaccount.com"
}
}

resource "hcp_vault_secrets_rotating_secret" "example_mongodb_atlas" {
app_name = "my-app-1"
secret_provider = "mongodb_atlas"
name = "my_mongodb_atlas_1"
integration_name = "my-mongodbatlas-1"
rotation_policy_name = "built-in:60-days-2-active"
mongodb_atlas_user = {
project_id = "<uuid>>"
database_name = "my-cluster-0"
roles = ["readWrite", "read"]
}
}

resource "hcp_vault_secrets_rotating_secret" "example_twilio" {
app_name = "my-app-1"
secret_provider = "twilio"
name = "my_twilio_1"
integration_name = "my-twilio-1"
rotation_policy_name = "built-in:60-days-2-active"
twilio_api_key = {}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/hcp-sdk-go v0.110.0
github.com/hashicorp/hcp-sdk-go v0.113.0
github.com/hashicorp/terraform-plugin-docs v0.19.4
github.com/hashicorp/terraform-plugin-framework v1.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC16
github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA=
github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI=
github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
github.com/hashicorp/hcp-sdk-go v0.110.0 h1:eaDO6XoEb0H+g00Ka3C+ZbRibhwWyA2YNmv48xFCL2w=
github.com/hashicorp/hcp-sdk-go v0.110.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/hashicorp/hcp-sdk-go v0.113.0 h1:JuA6mZosEezE550lNMEq43VLUCzVc+/jPmPC1Nd39Gk=
github.com/hashicorp/hcp-sdk-go v0.113.0/go.mod h1:vQ4fzdL1AmhIAbCw+4zmFe5Hbpajj3NvRWkJoVuxmAk=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res
vaultsecrets.NewVaultSecretsIntegrationGCPResource,
vaultsecrets.NewVaultSecretsIntegrationMongoDBAtlasResource,
vaultsecrets.NewVaultSecretsIntegrationTwilioResource,
vaultsecrets.NewVaultSecretsDynamicSecretResource,
vaultsecrets.NewVaultSecretsRotatingSecretResource,
// IAM
iam.NewServicePrincipalResource,
iam.NewServicePrincipalKeyResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,10 @@ func TestAcc_dataSourceVaultSecretsRotatingSecret(t *testing.T) {
}

reqBody := secretmodels.SecretServiceCreateMongoDBAtlasRotatingSecretBody{
SecretName: testSecretName,
RotationIntegrationName: testIntegrationName,
RotationPolicyName: "built-in:30-days-2-active",
MongodbGroupID: mongodbAtlasGroupID,
SecretName: testSecretName,
IntegrationName: testIntegrationName,
RotationPolicyName: "built-in:30-days-2-active",
MongodbGroupID: mongodbAtlasGroupID,
MongodbRoles: []*secretmodels.Secrets20231128MongoDBRole{
{
DatabaseName: mongodbAtlasDBName,
Expand Down
88 changes: 88 additions & 0 deletions internal/provider/vaultsecrets/dynamic_secret_aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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-provider-hcp/internal/clients"
)

var _ dynamicSecret = &awsDynamicSecret{}

type awsDynamicSecret struct{}

func (s *awsDynamicSecret) read(ctx context.Context, client secret_service.ClientService, secret *DynamicSecret) (any, error) {
response, err := client.GetAwsDynamicSecret(
secret_service.NewGetAwsDynamicSecretParamsWithContext(ctx).
WithOrganizationID(secret.OrganizationID.ValueString()).
WithProjectID(secret.ProjectID.ValueString()).
WithAppName(secret.AppName.ValueString()).
WithName(secret.Name.ValueString()), nil)
if err != nil && !clients.IsResponseCodeNotFound(err) {
return nil, err
}
if response == nil || response.Payload == nil {
return nil, nil
}
return response.Payload.Secret, nil
}

func (s *awsDynamicSecret) create(ctx context.Context, client secret_service.ClientService, secret *DynamicSecret) (any, error) {
if secret.AWSAssumeRole == nil {
return nil, fmt.Errorf("missing required field 'aws_assume_role'")
}

response, err := client.CreateAwsDynamicSecret(
secret_service.NewCreateAwsDynamicSecretParamsWithContext(ctx).
WithOrganizationID(secret.OrganizationID.ValueString()).
WithProjectID(secret.ProjectID.ValueString()).
WithAppName(secret.AppName.ValueString()).
WithBody(&secretmodels.SecretServiceCreateAwsDynamicSecretBody{
DefaultTTL: secret.DefaultTTL.ValueString(),
IntegrationName: secret.IntegrationName.ValueString(),
Name: secret.Name.ValueString(),
AssumeRole: &secretmodels.Secrets20231128AssumeRoleRequest{
RoleArn: secret.AWSAssumeRole.IAMRoleARN.ValueString(),
},
}),
nil)
if err != nil {
return nil, err
}
if response == nil || response.Payload == nil {
return nil, nil
}
return response.Payload.Secret, nil
}

func (s *awsDynamicSecret) update(ctx context.Context, client secret_service.ClientService, secret *DynamicSecret) (any, error) {
if secret.AWSAssumeRole == nil {
return nil, fmt.Errorf("missing required field 'aws_assume_role'")
}

response, err := client.UpdateAwsDynamicSecret(
secret_service.NewUpdateAwsDynamicSecretParamsWithContext(ctx).
WithOrganizationID(secret.OrganizationID.ValueString()).
WithProjectID(secret.ProjectID.ValueString()).
WithAppName(secret.AppName.ValueString()).
WithName(secret.Name.ValueString()).
WithBody(&secretmodels.SecretServiceUpdateAwsDynamicSecretBody{
DefaultTTL: secret.DefaultTTL.ValueString(),
AssumeRole: &secretmodels.Secrets20231128AssumeRoleRequest{
RoleArn: secret.AWSAssumeRole.IAMRoleARN.ValueString(),
},
}),
nil)
if err != nil {
return nil, err
}
if response == nil || response.Payload == nil {
return nil, nil
}
return response.Payload.Secret, nil
}
Loading

0 comments on commit 34b9c15

Please sign in to comment.