Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Fallback parameters matching Vault PR #2375

Merged
merged 9 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions internal/consts/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ const (
FieldSecretNameTemplate = "secret_name_template"
FieldIAMEndpoint = "iam_endpoint"
FieldSTSEndpoint = "sts_endpoint"
FieldSTSFallbackEndpoints = "sts_fallback_endpoints"
FieldIdentityTokenAudience = "identity_token_audience"
FieldIdentityTokenTTL = "identity_token_ttl"
FieldRoleArn = "role_arn"
Expand Down Expand Up @@ -417,6 +418,7 @@ const (
FieldGranularityLevel = "granularity_level"
FieldEC2Endpoint = "ec2_endpoint"
FieldSTSRegion = "sts_region"
FieldSTSFallbackRegions = "sts_fallback_regions"
FieldIAMServerIDHeaderValue = "iam_server_id_header_value"
FieldListingVisibility = "listing_visibility"
FieldPassthroughRequestHeaders = "passthrough_request_headers"
Expand Down Expand Up @@ -528,6 +530,7 @@ const (
VaultVersion116 = "1.16.0"
VaultVersion117 = "1.17.0"
VaultVersion118 = "1.18.0"
VaultVersion119 = "1.19.0"

/*
Vault auth methods
Expand Down
1 change: 1 addition & 0 deletions internal/provider/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var (
VaultVersion116 = version.Must(version.NewSemver(consts.VaultVersion116))
VaultVersion117 = version.Must(version.NewSemver(consts.VaultVersion117))
VaultVersion118 = version.Must(version.NewSemver(consts.VaultVersion118))
VaultVersion119 = version.Must(version.NewSemver(consts.VaultVersion119))

TokenTTLMinRecommended = time.Minute * 15
)
Expand Down
79 changes: 77 additions & 2 deletions vault/resource_aws_secret_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ func awsSecretBackendResource() *schema.Resource {
Optional: true,
Description: "Specifies a custom HTTP STS endpoint to use.",
},
consts.FieldSTSRegion: {
kpcraig marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Optional: true,
Description: "Specifies a custom STS region to use.",
},
consts.FieldSTSFallbackEndpoints: {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "Specifies a list of custom STS fallback endpoints to use (in order).",
},
consts.FieldSTSFallbackRegions: {
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Description: "Specifies a list of custom STS fallback regions to use (in order).",
},
consts.FieldUsernameTemplate: {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -163,6 +180,9 @@ func getMountCustomizeDiffFunc(field string) schema.CustomizeDiffFunc {
}

func awsSecretBackendCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
useAPIVer119 := provider.IsAPISupported(meta, provider.VaultVersion119)
useAPIVer116 := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta)

client, e := provider.GetClient(d, meta)
if e != nil {
return diag.FromErr(e)
Expand All @@ -183,7 +203,7 @@ func awsSecretBackendCreate(ctx context.Context, d *schema.ResourceData, meta in
DefaultLeaseTTL: fmt.Sprintf("%ds", defaultTTL),
MaxLeaseTTL: fmt.Sprintf("%ds", maxTTL),
}
useAPIVer116 := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta)

if useAPIVer116 {
identityTokenKey := d.Get(consts.FieldIdentityTokenKey).(string)
if identityTokenKey != "" {
Expand All @@ -207,12 +227,27 @@ func awsSecretBackendCreate(ctx context.Context, d *schema.ResourceData, meta in
consts.FieldAccessKey: accessKey,
consts.FieldSecretKey: secretKey,
}

for _, k := range awsSecretFields {
if v, ok := d.GetOk(k); ok {
data[k] = v.(string)
}
}

if useAPIVer119 {
if v, ok := d.GetOk(consts.FieldSTSFallbackEndpoints); ok {
data[consts.FieldSTSFallbackEndpoints] = util.ToStringArray(v.([]interface{}))
}

if v, ok := d.GetOk(consts.FieldSTSFallbackRegions); ok {
data[consts.FieldSTSFallbackRegions] = util.ToStringArray(v.([]interface{}))
}

if v, ok := d.GetOk(consts.FieldSTSRegion); ok {
data[consts.FieldSTSRegion] = v.(string)
}
}

if useAPIVer116 {
if v, ok := d.GetOk(consts.FieldIdentityTokenAudience); ok && v != "" {
data[consts.FieldIdentityTokenAudience] = v.(string)
Expand Down Expand Up @@ -244,6 +279,7 @@ func awsSecretBackendCreate(ctx context.Context, d *schema.ResourceData, meta in

func awsSecretBackendRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
useAPIVer116 := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta)
useAPIVer119 := provider.IsAPISupported(meta, provider.VaultVersion119)

client, e := provider.GetClient(d, meta)
if e != nil {
Expand Down Expand Up @@ -304,6 +340,26 @@ func awsSecretBackendRead(ctx context.Context, d *schema.ResourceData, meta inte
}
}

if useAPIVer119 {
if v, ok := resp.Data[consts.FieldSTSFallbackEndpoints]; ok {
if err := d.Set(consts.FieldSTSFallbackEndpoints, v); err != nil {
return diag.Errorf("error reading %s for AWS Secret Backend %q: %q", consts.FieldSTSFallbackEndpoints, path, err)
}
}

if v, ok := resp.Data[consts.FieldSTSFallbackRegions]; ok {
if err := d.Set(consts.FieldSTSFallbackRegions, v); err != nil {
return diag.Errorf("error reading %s for AWS Secret Backend %q: %q", consts.FieldSTSFallbackRegions, path, err)
}
}

if v, ok := resp.Data[consts.FieldSTSRegion]; ok {
if err := d.Set(consts.FieldSTSRegion, v); err != nil {
return diag.Errorf("error reading %s for AWS Secret Backend %q: %q", consts.FieldSTSRegion, path, err)
}
}
}

if useAPIVer116 {
if err := d.Set(consts.FieldIdentityTokenAudience, resp.Data[consts.FieldIdentityTokenAudience]); err != nil {
return diag.Errorf("error reading %s for AWS Secret Backend %q: %q", consts.FieldIdentityTokenAudience, path, err)
Expand Down Expand Up @@ -343,6 +399,7 @@ func awsSecretBackendRead(ctx context.Context, d *schema.ResourceData, meta inte

func awsSecretBackendUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
useAPIVer116 := provider.IsAPISupported(meta, provider.VaultVersion116) && provider.IsEnterpriseSupported(meta)
useAPIVer119 := provider.IsAPISupported(meta, provider.VaultVersion119)

client, e := provider.GetClient(d, meta)
if e != nil {
Expand Down Expand Up @@ -377,7 +434,11 @@ func awsSecretBackendUpdate(ctx context.Context, d *schema.ResourceData, meta in
}
log.Printf("[DEBUG] Updated mount config input for %q", path)
}
if d.HasChanges(consts.FieldAccessKey, consts.FieldSecretKey, consts.FieldRegion, consts.FieldIAMEndpoint, consts.FieldSTSEndpoint, consts.FieldIdentityTokenTTL, consts.FieldIdentityTokenAudience, consts.FieldRoleArn) {
if d.HasChanges(consts.FieldAccessKey,
consts.FieldSecretKey, consts.FieldRegion, consts.FieldIAMEndpoint,
consts.FieldSTSEndpoint, consts.FieldSTSFallbackEndpoints, consts.FieldSTSRegion, consts.FieldSTSFallbackRegions,
consts.FieldIdentityTokenTTL, consts.FieldIdentityTokenAudience, consts.FieldRoleArn,
) {
log.Printf("[DEBUG] Updating root credentials at %q", path+"/config/root")
data := map[string]interface{}{
consts.FieldAccessKey: d.Get(consts.FieldAccessKey).(string),
Expand All @@ -390,6 +451,20 @@ func awsSecretBackendUpdate(ctx context.Context, d *schema.ResourceData, meta in
}
}

if useAPIVer119 {
if v, ok := d.GetOk(consts.FieldSTSFallbackEndpoints); ok {
data[consts.FieldSTSFallbackEndpoints] = util.ToStringArray(v.([]interface{}))
}

if v, ok := d.GetOk(consts.FieldSTSFallbackRegions); ok {
data[consts.FieldSTSFallbackRegions] = util.ToStringArray(v.([]interface{}))
}

if v, ok := d.GetOk(consts.FieldSTSRegion); ok {
data[consts.FieldSTSRegion] = v.(string)
}
}

if useAPIVer116 {
identityTokenAudience := d.Get(consts.FieldIdentityTokenAudience).(string)
if identityTokenAudience != "" {
Expand Down
84 changes: 84 additions & 0 deletions vault/resource_aws_secret_backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,51 @@ func TestAccAWSSecretBackend_basic(t *testing.T) {
})
}

func TestAccAWSSecretBackend_fallback(t *testing.T) {
vinay-gopalan marked this conversation as resolved.
Show resolved Hide resolved
path := acctest.RandomWithPrefix("tf-test-aws")
resourceType := "vault_aws_secret_backend"
resourceName := resourceType + ".test"
accessKey, secretKey := testutil.GetTestAWSCreds(t)
resource.Test(t, resource.TestCase{
ProviderFactories: providerFactories,
PreCheck: func() { testutil.TestAccPreCheck(t) },
CheckDestroy: testCheckMountDestroyed(resourceType, consts.MountTypeAWS, consts.FieldPath),
Steps: []resource.TestStep{
{
Config: testAccAWSSecretBackendConfig_fallback(path, accessKey, secretKey),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "test description"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSEndpoint, "https://sts.us-west-1.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSRegion, "us-west-1"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".0", "us-east-2"),
vinay-gopalan marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".1", "us-east-1"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".#", "2"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".0", "https://sts.us-east-2.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".1", "https://sts.us-east-1.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".#", "2"),
),
},
testutil.GetImportTestStep(resourceName, false, nil, consts.FieldSecretKey, consts.FieldDisableRemount),
{
Config: testAccAWSSecretBackendConfig_fallbackUpdated(path, accessKey, secretKey),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, consts.FieldPath, path),
resource.TestCheckResourceAttr(resourceName, consts.FieldDescription, "updated description"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSEndpoint, "https://sts.us-central-2.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSRegion, "us-central-2"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".0", "us-east-2"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".1", "eu-central-1"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackRegions+".#", "2"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".0", "https://sts.us-east-2.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".1", "https://sts.eu-central-1.amazonaws.com"),
resource.TestCheckResourceAttr(resourceName, consts.FieldSTSFallbackEndpoints+".#", "2"),
),
},
},
})
}

func TestAccAWSSecretBackend_wif(t *testing.T) {
path := acctest.RandomWithPrefix("tf-test-aws")
resourceType := "vault_aws_secret_backend"
Expand Down Expand Up @@ -194,6 +239,45 @@ resource "vault_aws_secret_backend" "test" {
}`, path, accessKey, secretKey)
}

func testAccAWSSecretBackendConfig_fallback(path, accessKey, secretKey string) string {
return fmt.Sprintf(`
resource "vault_aws_secret_backend" "test" {
path = "%s"
description = "test description"
default_lease_ttl_seconds = 1800
max_lease_ttl_seconds = 43200
access_key = "%s"
secret_key = "%s"
region = "us-west-1"

iam_endpoint = "https://iam.amazonaws.com"
sts_endpoint = "https://sts.us-west-1.amazonaws.com"

sts_region = "us-west-1"
sts_fallback_regions = ["us-east-2", "us-east-1"]
sts_fallback_endpoints = ["https://sts.us-east-2.amazonaws.com","https://sts.us-east-1.amazonaws.com"]
}`, path, accessKey, secretKey)
}

func testAccAWSSecretBackendConfig_fallbackUpdated(path, accessKey, secretKey string) string {
return fmt.Sprintf(`
resource "vault_aws_secret_backend" "test" {
path = "%s"
description = "updated description"
default_lease_ttl_seconds = 60
max_lease_ttl_seconds = 1000
access_key = "%s"
secret_key = "%s"
region = "us-central-2"

sts_endpoint = "https://sts.us-central-2.amazonaws.com"

sts_region = "us-central-2"
sts_fallback_regions = ["us-east-2", "eu-central-1"]
sts_fallback_endpoints = ["https://sts.us-east-2.amazonaws.com","https://sts.eu-central-1.amazonaws.com"]
}`, path, accessKey, secretKey)
}

func testAccAWSSecretBackendConfig_wifBasic(path string) string {
return fmt.Sprintf(`
resource "vault_aws_secret_backend" "test" {
Expand Down
6 changes: 6 additions & 0 deletions website/docs/r/aws_secret_backend.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ for credentials issued by this backend.

* `sts_endpoint` - (Optional) Specifies a custom HTTP STS endpoint to use.

* `sts_region` - (Optional) Specifies the region of the STS endpoint. Should be included if `sts_endpoint` is supplied. Requires Vault 1.19+

* `sts_fallback_endpoints` - (Optional) Ordered list of `sts_endpoint`s to try if the defined one fails. Requires Vault 1.19+

* `sts_fallback_regions` - (Optional) Ordered list of `sts_region`s matching the fallback endpoints. Should correspond in order with those endpoints. Requires Vault 1.19+

* `username_template` - (Optional) Template describing how dynamic usernames are generated. The username template is used to generate both IAM usernames (capped at 64 characters) and STS usernames (capped at 32 characters). If no template is provided the field defaults to the template:

* `local` - (Optional) Specifies whether the secrets mount will be marked as local. Local mounts are not replicated to performance replicas.
Expand Down
Loading