Backport VAULT-43374: adding aws sdk tests into ce/main (#14089)

* VAULT-43374: adding aws sdk tests (#13547)

* adding aws sdk tests

* testing pipeline

* fixing copyright

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* rebasing

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* cleaning up

* cleaning up

* cleaning up

* cleaning up

* cleaning up

* testing pipeline

* testing pipeline

* testing pipeline

* fixing lint error

* addressing comments

* addressing comments

* fixing error

* testing pipeline

* testing pipeline

* addressing comments in helpers.go

* testing pipeline

* updating delete user logic

* updating delete user logic

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* addressing comments

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* testing pipeline

* undo testing

* Go mod

---------

Co-authored-by: Tin Vo <tintvo08@gmail.com>
Co-authored-by: LT Carbonell <ltcarbonell@pm.me>
This commit is contained in:
Vault Automation 2026-04-17 17:08:15 -04:00 committed by GitHub
parent 7f0c0dbdad
commit 1fc1b56e22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 391 additions and 13 deletions

11
go.mod
View File

@ -54,6 +54,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/aws/aws-sdk-go v1.55.8
github.com/aws/aws-sdk-go-v2/config v1.32.14
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8
github.com/cenkalti/backoff/v4 v4.3.0
github.com/chrismalek/oktasdk-go v0.0.0-20181212195951-3430665dfaa0
github.com/cockroachdb/cockroach-go/v2 v2.3.8
@ -333,14 +334,14 @@ require (
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apache/arrow-go/v18 v18.4.0 // indirect
github.com/avast/retry-go/v4 v4.6.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.6
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 // indirect
@ -352,8 +353,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
github.com/aws/smithy-go v1.24.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10
github.com/aws/smithy-go v1.25.0 // indirect
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/benbjohnson/immutable v0.4.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect

18
go.sum
View File

@ -209,8 +209,8 @@ github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBea
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2 v1.41.6 h1:1AX0AthnBQzMx1vbmir3Y4WsnJgiydmnJjiLu+LvXOg=
github.com/aws/aws-sdk-go-v2 v1.41.6/go.mod h1:dy0UzBIfwSeot4grGvY1AqFWN5zgziMmWGzysDnHFcQ=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI=
@ -223,10 +223,10 @@ github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21 h1:HFn8sVT87KWnGs2Q2gO/brP
github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.6.21/go.mod h1:BGZ/K6gLGJt8K36j6gcsD7WVxmWt0MGBYtr57iLweio=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13 h1:uMC4oL6G3MNhodo358QEqSDjrgvzV3TUQ58nyQSGq2E=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.13/go.mod h1:Cer86AE2686DvVUe57LPve3jUBmbujuaonSX8pNzGgw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 h1:GmLa5Kw1ESqtFpXsx5MmC84QWa/ZrLZvlJGa2y+4kcQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22/go.mod h1:6sW9iWm9DK9YRpRGga/qzrzNLgKpT2cIxb7Vo2eNOp0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 h1:dY4kWZiSaXIzxnKlj17nHnBcXXBfac6UlsAx2qL6XrU=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22/go.mod h1:KIpEUx0JuRZLO7U6cbV204cWAEco2iC3l061IxlwLtI=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
@ -235,6 +235,8 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 h1:A+7NViqbMUCoTQFWjbSXdbzE4K5
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0/go.mod h1:R+2BNtUfTfhPY0RH18oL02q116bakeBWjanrbnVBqkM=
github.com/aws/aws-sdk-go-v2/service/ecs v1.77.0 h1:g3RYQmK6uRU5kOuwDthemuiiTbmwyGd8Wzf+k7cYWtk=
github.com/aws/aws-sdk-go-v2/service/ecs v1.77.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8 h1:p0oB4eZfBfBAOasnKvHJOlNcuHVE/ieuWs7uIZgQlyQ=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.8/go.mod h1:epCaPnGVdiX5ra1lHPfRkVuiQGxrdY8bRI2FBJU+6ok=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
@ -253,8 +255,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6f
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
github.com/aws/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg=
github.com/aws/smithy-go v1.24.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/aws/smithy-go v1.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U=
github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA=

View File

@ -26,6 +26,14 @@ func (s *Session) MustRead(path string) *api.Secret {
return secret
}
func (s *Session) MustList(path string) *api.Secret {
s.t.Helper()
secret, err := s.Client.Logical().List(path)
require.NoError(s.t, err)
return secret
}
// MustReadRequired is a stricter version of MustRead that fails if a 404/nil is returned
func (s *Session) MustReadRequired(path string) *api.Secret {
s.t.Helper()

View File

@ -0,0 +1,265 @@
// Copyright IBM Corp. 2025, 2026
// SPDX-License-Identifier: BUSL-1.1
package aws
import (
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/iam"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
// getPolicyArnByName returns the ARN for a policy with the given name.
func getPolicyArnByName(ctx context.Context, iamClient *iam.Client, policyName string) (string, error) {
paginator := iam.NewListPoliciesPaginator(iamClient, &iam.ListPoliciesInput{Scope: "All"})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
return "", err
}
for _, p := range page.Policies {
if aws.ToString(p.PolicyName) == policyName {
return aws.ToString(p.Arn), nil
}
}
}
return "", fmt.Errorf("policy %s not found", policyName)
}
// getRoleArnByName returns the ARN for a role with the given name.
func getRoleArnByName(ctx context.Context, iamClient *iam.Client, roleName string) (string, error) {
paginator := iam.NewListRolesPaginator(iamClient, &iam.ListRolesInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
return "", err
}
for _, r := range page.Roles {
if aws.ToString(r.RoleName) == roleName {
return aws.ToString(r.Arn), nil
}
}
}
return "", fmt.Errorf("role %s not found", roleName)
}
// createTestIAMUser creates a new IAM user with a unique name, attaches the DemoUser policy, and returns the user/access key info and AWS region.
func createTestIAMUser(t *testing.T) (
userName string,
accessKeyID string,
secretAccessKey string,
demoUserPolicyArn string,
assumedRoleArn string,
awsRegion string,
) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
t.Fatalf("failed to load AWS config: %v", err)
}
awsRegion = cfg.Region
if awsRegion == "" {
t.Fatalf("AWS region is empty in config")
}
iamClient := iam.NewFromConfig(cfg)
stsClient := sts.NewFromConfig(cfg)
// Get current AWS account identity (for unique name)
caller, err := stsClient.GetCallerIdentity(ctx, &sts.GetCallerIdentityInput{})
if err != nil {
t.Fatalf("failed to get caller identity: %v", err)
}
accountID := aws.ToString(caller.Account)
// Generate a random hex suffix for uniqueness
const randomSuffixByteLength = 4
suffix := make([]byte, randomSuffixByteLength)
if _, err := rand.Read(suffix); err != nil {
t.Fatalf("failed to generate random suffix: %v", err)
}
hexSuffix := hex.EncodeToString(suffix)
userName = fmt.Sprintf("demo-GitHubActions-%s-%s", accountID, hexSuffix)
// Lookup DemoUser policy ARN
demoUserPolicyArn, err = getPolicyArnByName(ctx, iamClient, "DemoUser")
if err != nil {
t.Fatalf("DemoUser policy not found: %v", err)
}
// Lookup vault-assumed-role-credentials-demo role ARN
assumedRoleArn, err = getRoleArnByName(ctx, iamClient, "vault-assumed-role-credentials-demo")
if err != nil {
t.Fatalf("vault-assumed-role-credentials-demo role not found: %v", err)
}
// Create IAM user
_, err = iamClient.CreateUser(ctx, &iam.CreateUserInput{
UserName: aws.String(userName),
PermissionsBoundary: aws.String(demoUserPolicyArn),
})
if err != nil {
t.Fatalf("failed to create IAM user: %v", err)
}
// Attach policy to user
_, err = iamClient.AttachUserPolicy(ctx, &iam.AttachUserPolicyInput{
UserName: aws.String(userName),
PolicyArn: aws.String(demoUserPolicyArn),
})
if err != nil {
t.Fatalf("failed to attach policy: %v", err)
}
// Create access key
keyOut, err := iamClient.CreateAccessKey(ctx, &iam.CreateAccessKeyInput{
UserName: aws.String(userName),
})
if err != nil {
t.Fatalf("failed to create access key: %v", err)
}
accessKeyID = aws.ToString(keyOut.AccessKey.AccessKeyId)
secretAccessKey = aws.ToString(keyOut.AccessKey.SecretAccessKey)
// IAM is eventually consistent; wait briefly before verifying the user is readable.
t.Logf("Verifying IAM user %s exists...", userName)
waitTime := 10 * time.Second
verifyDeadline := time.Now().Add(waitTime * 2)
var lastErr error
for time.Now().Before(verifyDeadline) {
time.Sleep(waitTime)
_, lastErr = iamClient.GetUser(ctx, &iam.GetUserInput{UserName: aws.String(userName)})
if lastErr == nil {
break
}
t.Logf("IAM user %q not readable yet; retrying: %v", userName, lastErr)
}
if lastErr != nil {
t.Fatalf("failed to verify IAM user %q: %v", userName, lastErr)
}
return userName, accessKeyID, secretAccessKey, demoUserPolicyArn, assumedRoleArn, awsRegion
}
// getAwsUsernameTemplate returns the username template string for Vault AWS config.
func getAwsUsernameTemplate(awsUserName string) string {
const prefix = `{{ if (eq .Type "STS") }}{{ printf "`
const stsSuffix = `-%s-%s" (random 20) (unix_time) | truncate 32 }}{{ else }}{{ printf "`
const iamUserSuffix = `-%s-%s" (unix_time) (random 20) | truncate 60 }}{{ end }}`
return prefix + awsUserName + stsSuffix + awsUserName + iamUserSuffix
}
// getAllowDescribeRegionsPolicy returns a policy document allowing ec2:DescribeRegions.
func getAllowDescribeRegionsPolicy() string {
return `{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["ec2:DescribeRegions"],
"Resource": ["*"]
}
]
}`
}
// deleteIAMUserByAccessKey deletes the IAM user that owns the given access key.
func deleteIAMUserByAccessKey(t *testing.T, targetAccessKeyID string) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
t.Fatalf("failed to load AWS config: %v", err)
}
iamClient := iam.NewFromConfig(cfg)
paginator := iam.NewListUsersPaginator(iamClient, &iam.ListUsersInput{})
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
t.Fatalf("failed to list IAM users: %v", err)
}
for _, user := range page.Users {
userName := aws.ToString(user.UserName)
if !strings.Contains(userName, "demo-GitHubActions") {
continue
}
// List all access keys for this user
keyPaginator := iam.NewListAccessKeysPaginator(iamClient, &iam.ListAccessKeysInput{
UserName: &userName,
})
for keyPaginator.HasMorePages() {
keyPage, err := keyPaginator.NextPage(ctx)
if err != nil {
t.Logf("warning: failed to list access keys for user %q: %v", userName, err)
continue
}
for _, key := range keyPage.AccessKeyMetadata {
accessKeyId := aws.ToString(key.AccessKeyId)
if accessKeyId == targetAccessKeyID {
// Found the user with the target access key. Detach managed policies first,
// then delete all access keys, then delete the user.
policyPaginator := iam.NewListAttachedUserPoliciesPaginator(iamClient, &iam.ListAttachedUserPoliciesInput{
UserName: &userName,
})
for policyPaginator.HasMorePages() {
policyPage, err := policyPaginator.NextPage(ctx)
if err != nil {
t.Logf("warning: failed to list attached policies for user %q: %v", userName, err)
continue
}
for _, policy := range policyPage.AttachedPolicies {
policyArn := aws.ToString(policy.PolicyArn)
if _, err := iamClient.DetachUserPolicy(ctx, &iam.DetachUserPolicyInput{
UserName: &userName,
PolicyArn: &policyArn,
}); err != nil {
t.Logf("warning: failed to detach policy %q from user %q: %v", policyArn, userName, err)
}
}
}
keyPaginator2 := iam.NewListAccessKeysPaginator(iamClient, &iam.ListAccessKeysInput{
UserName: &userName,
})
for keyPaginator2.HasMorePages() {
keyPage2, err := keyPaginator2.NextPage(ctx)
if err != nil {
t.Logf("warning: failed to list access keys for cleanup on user %q: %v", userName, err)
continue
}
for _, key2 := range keyPage2.AccessKeyMetadata {
accessKeyId2 := aws.ToString(key2.AccessKeyId)
if _, err := iamClient.DeleteAccessKey(ctx, &iam.DeleteAccessKeyInput{
UserName: &userName,
AccessKeyId: &accessKeyId2,
}); err != nil {
t.Logf("warning: failed to delete access key %q for user %q: %v", accessKeyId2, userName, err)
}
}
}
// Delete the user
if _, err := iamClient.DeleteUser(ctx, &iam.DeleteUserInput{
UserName: &userName,
}); err != nil {
t.Logf("warning: failed to delete user %q: %v", userName, err)
}
return
}
}
}
}
}
}

View File

@ -0,0 +1,102 @@
// Copyright IBM Corp. 2025, 2026
// SPDX-License-Identifier: BUSL-1.1
package aws
import (
"fmt"
"os"
"testing"
"time"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/sdk/helper/testcluster/blackbox"
)
// TestAWS_GenerateNewUser verifies AWS secrets engine can generate IAM user credentials.
func TestAWS_GenerateNewUser(t *testing.T) {
t.Parallel()
v := blackbox.New(t)
accessKey := os.Getenv("AWS_ACCESS_KEY_ID")
secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
if accessKey == "" || secretKey == "" {
t.Log("AWS credentials not available - skipping AWS secrets engine test")
t.Skip("AWS credentials not available - skipping AWS secrets engine test")
}
t.Logf("Creating test IAM user via helpers.go...")
userName, tempAccessKeyId, tempSecretAccessKey, demoUserPolicyArn, _, _ := createTestIAMUser(t)
t.Logf("Created test IAM user: %s", userName)
var newAccessKey string
t.Cleanup(func() {
if newAccessKey != "" {
t.Logf("Cleanup: deleting IAM user created by Vault with access key: %s", newAccessKey)
deleteIAMUserByAccessKey(t, newAccessKey)
}
t.Logf("Cleanup: deleting IAM user by initial access key: %s", tempAccessKeyId)
deleteIAMUserByAccessKey(t, tempAccessKeyId)
})
path := fmt.Sprintf("aws-test-%d", time.Now().UnixNano())
t.Logf("Enabling AWS secrets engine at path: %s", path)
v.MustEnableSecretsEngine(path, &api.MountInput{Type: "aws"})
t.Logf("Configuring AWS secrets engine with root credentials and username template for user: %s", userName)
v.MustWrite(fmt.Sprintf("%s/config/root", path), map[string]any{
"access_key": tempAccessKeyId,
"secret_key": tempSecretAccessKey,
"region": "us-east-1",
"username_template": getAwsUsernameTemplate(userName),
})
roleName := "aws-enos-role"
t.Logf("Creating Vault AWS role: %s", roleName)
v.MustWrite(fmt.Sprintf("%s/roles/%s", path, roleName), map[string]any{
"credential_type": "iam_user",
"permissions_boundary_arn": demoUserPolicyArn,
"policy_document": getAllowDescribeRegionsPolicy(),
})
t.Logf("Reading and verifying AWS role configuration for role: %s", roleName)
roleResp := v.MustRead(fmt.Sprintf("%s/roles/%s", path, roleName))
if roleResp.Data == nil {
t.Fatal("Expected to read AWS role configuration")
}
t.Logf("Listing AWS roles at path: %s/roles", path)
rolesList := v.MustList(fmt.Sprintf("%s/roles", path))
if rolesList == nil || rolesList.Data == nil {
t.Fatal("No AWS roles created! (rolesList is nil or Data is nil)")
}
roleKeys, ok := rolesList.Data["keys"].([]interface{})
if !ok || len(roleKeys) == 0 {
t.Fatal("No AWS roles created! (rolesList.Data['keys'] is empty or not a slice)")
}
t.Logf("Found AWS roles: %v", roleKeys)
t.Logf("Reading root config to verify username template is set correctly")
rootUser := v.MustRead(fmt.Sprintf("%s/config/root", path))
if rootUser == nil || rootUser.Data == nil {
t.Fatalf("Expected to read root config, got nil: %#v", rootUser)
}
if val, ok := rootUser.Data["username_template"]; !ok || val == nil {
t.Fatalf("username_template missing in root config: %#v", rootUser)
}
t.Logf("Generating new credentials for IAM user using role: %s", roleName)
newUser := v.MustRead(fmt.Sprintf("%s/creds/%s", path, roleName))
if newUser == nil || newUser.Data == nil {
t.Fatalf("Failed to generate new credentials for IAM user: %s", roleName)
}
if val, ok := newUser.Data["access_key"]; !ok || val == nil || val == tempAccessKeyId {
t.Fatalf("The new access key is empty or is matching the old one: %v", val)
}
newAccessKey, ok = newUser.Data["access_key"].(string)
if !ok || newAccessKey == "" {
t.Fatalf("Could not extract access_key from new credentials: %v", newUser.Data["access_key"])
}
t.Logf("Captured Vault-created access key for cleanup: %s", newAccessKey)
}