Skip to content

Commit 3d572f0

Browse files
authored
vault_azure_access_credentials: fix validate_creds bug (hashicorp#2086)
* vault_azure_access_credentials: fix validate_creds bug * add changelog * rename var
1 parent 4b1926f commit 3d572f0

File tree

4 files changed

+100
-16
lines changed

4 files changed

+100
-16
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## Unreleased
22

33
BUGS:
4-
54
* Fix `vault_identity_group` loses externally managed policies on updates when `external_policies = true` ([#2084](https://github.com/hashicorp/terraform-provider-vault/pull/2084))
5+
* Fix regression in `vault_azure_access_credentials` where we returned prematurely on 401 responses:([#2086](https://github.com/hashicorp/terraform-provider-vault/pull/2086))
66

77
## 3.22.0 (Nov 1, 2023)
88

testutil/testutil.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func GetTestAWSRegion(t *testing.T) string {
127127
}
128128

129129
type AzureTestConf struct {
130-
SubscriptionID, TenantID, ClientID, ClientSecret, Scope string
130+
SubscriptionID, TenantID, ClientID, ClientSecret, Scope, AppObjectID string
131131
}
132132

133133
func GetTestAzureConf(t *testing.T) *AzureTestConf {
@@ -147,6 +147,23 @@ func GetTestAzureConf(t *testing.T) *AzureTestConf {
147147
}
148148
}
149149

150+
func GetTestAzureConfExistingSP(t *testing.T) *AzureTestConf {
151+
v := SkipTestEnvUnset(t,
152+
"AZURE_SUBSCRIPTION_ID",
153+
"AZURE_TENANT_ID",
154+
"AZURE_CLIENT_ID",
155+
"AZURE_CLIENT_SECRET",
156+
"AZURE_APPLICATION_OBJECT_ID")
157+
158+
return &AzureTestConf{
159+
SubscriptionID: v[0],
160+
TenantID: v[1],
161+
ClientID: v[2],
162+
ClientSecret: v[3],
163+
AppObjectID: v[4],
164+
}
165+
}
166+
150167
func GetTestGCPCreds(t *testing.T) (string, string) {
151168
t.Helper()
152169

vault/data_source_azure_access_credentials.go

+17-14
Original file line numberDiff line numberDiff line change
@@ -238,49 +238,52 @@ func azureAccessCredentialsDataSourceRead(ctx context.Context, d *schema.Resourc
238238
return diag.Errorf("failed to create providers client: %s", err)
239239
}
240240
delay := time.Duration(d.Get("num_seconds_between_tests").(int)) * time.Second
241-
endTime := time.Now().Add(
242-
time.Duration(d.Get("max_cred_validation_seconds").(int)) * time.Second)
241+
maxValidationSeconds := d.Get("max_cred_validation_seconds").(int)
242+
endTime := time.Now().Add(time.Duration(maxValidationSeconds) * time.Second)
243243
wantSuccessCount := d.Get("num_sequential_successes").(int)
244244
var successCount int
245+
// begin validate_creds retry loop
245246
for {
246247
pager := providerClient.NewListPager(&armresources.ProvidersClientListOptions{
247248
Expand: pointerutil.StringPtr("metadata"),
248249
})
249250

250-
var providers []*armresources.Provider
251+
hasError := false
251252
for pager.More() {
252253
// capture raw response so we can get the status code
253254
var rawResponse *http.Response
254255
ctxWithResp := runtime.WithCaptureResponse(ctx, &rawResponse)
255256

256-
nextResult, err := pager.NextPage(ctxWithResp)
257+
_, err := pager.NextPage(ctxWithResp)
257258
if err != nil {
259+
hasError = true
258260
log.Printf("[WARN] Provider Client List request failed err=%s", err)
259-
}
260-
if rawResponse.StatusCode == http.StatusUnauthorized {
261-
return diag.Errorf("validation failed, unauthorized credentials from Vault, err=%s", err)
261+
// ensure we don't loop forever
262+
break
262263
}
263264

265+
// log the response status code and headers
264266
log.Printf("[DEBUG] Provider Client List response %+v", rawResponse)
265-
266-
if nextResult.Value != nil {
267-
providers = append(providers, nextResult.Value...)
268-
}
269267
}
270268

271-
if len(providers) != 0 {
269+
if !hasError {
272270
successCount++
273271
log.Printf("[DEBUG] Credential validation succeeded on try %d/%d", successCount, wantSuccessCount)
274272
if successCount >= wantSuccessCount {
275273
break
276274
}
277275
} else {
278-
log.Printf("[WARN] Credential validation failed with %s, retrying in %s", err, delay)
276+
log.Printf("[WARN] Credential validation failed, retrying in %s", delay)
279277
successCount = 0
280278
}
281279

282280
if time.Now().After(endTime) {
283-
return diag.Errorf("validation failed after max_cred_validation_seconds, giving up; now=%s, endTime=%s", time.Now().String(), endTime.String())
281+
return diag.Errorf(
282+
"validation failed after max_cred_validation_seconds of %d, giving up; now=%s, endTime=%s",
283+
maxValidationSeconds,
284+
time.Now().String(),
285+
endTime.String(),
286+
)
284287
}
285288

286289
time.Sleep(delay)

vault/data_source_azure_access_credentials_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/hashicorp/terraform-provider-vault/testutil"
1515
)
1616

17+
// TestAccDataSourceAzureAccessCredentials_basic tests the creation of dynamic
18+
// service principals
1719
func TestAccDataSourceAzureAccessCredentials_basic(t *testing.T) {
1820
// This test takes a while because it's testing a loop that
1921
// retries real credentials until they're eventually consistent.
@@ -38,6 +40,68 @@ func TestAccDataSourceAzureAccessCredentials_basic(t *testing.T) {
3840
})
3941
}
4042

43+
// TestAccDataSourceAzureAccessCredentials_basic tests the credential
44+
// generation for existing service principals
45+
func TestAccDataSourceAzureAccessCredentials_ExistingSP(t *testing.T) {
46+
// This test takes a while because it's testing a loop that
47+
// retries real credentials until they're eventually consistent.
48+
if testing.Short() {
49+
t.SkipNow()
50+
}
51+
mountPath := acctest.RandomWithPrefix("tf-test-azure")
52+
conf := testutil.GetTestAzureConfExistingSP(t)
53+
resource.Test(t, resource.TestCase{
54+
ProviderFactories: providerFactories,
55+
PreCheck: func() { testutil.TestAccPreCheck(t) },
56+
Steps: []resource.TestStep{
57+
{
58+
Config: testAccDataSourceAzureAccessCredentialsConfig_existingSP(mountPath, conf, 60),
59+
Check: resource.ComposeTestCheckFunc(
60+
resource.TestCheckResourceAttrSet("data.vault_azure_access_credentials.test", "client_id"),
61+
resource.TestCheckResourceAttrSet("data.vault_azure_access_credentials.test", "client_secret"),
62+
resource.TestCheckResourceAttrSet("data.vault_azure_access_credentials.test", "lease_id"),
63+
),
64+
},
65+
},
66+
})
67+
}
68+
69+
func testAccDataSourceAzureAccessCredentialsConfig_existingSP(mountPath string, conf *testutil.AzureTestConf, maxSecs int) string {
70+
template := `
71+
resource "vault_azure_secret_backend" "test" {
72+
path = "{{mountPath}}"
73+
subscription_id = "{{subscriptionID}}"
74+
tenant_id = "{{tenantID}}"
75+
client_id = "{{clientID}}"
76+
client_secret = "{{clientSecret}}"
77+
}
78+
79+
resource "vault_azure_secret_backend_role" "test" {
80+
backend = vault_azure_secret_backend.test.path
81+
role = "my-role"
82+
application_object_id = "{{appObjectID}}"
83+
ttl = 300
84+
max_ttl = 600
85+
}
86+
87+
data "vault_azure_access_credentials" "test" {
88+
backend = vault_azure_secret_backend.test.path
89+
role = vault_azure_secret_backend_role.test.role
90+
validate_creds = true
91+
num_seconds_between_tests = 1
92+
max_cred_validation_seconds = {{maxCredValidationSeconds}}
93+
}`
94+
95+
parsed := strings.Replace(template, "{{mountPath}}", mountPath, -1)
96+
parsed = strings.Replace(parsed, "{{subscriptionID}}", conf.SubscriptionID, -1)
97+
parsed = strings.Replace(parsed, "{{tenantID}}", conf.TenantID, -1)
98+
parsed = strings.Replace(parsed, "{{clientID}}", conf.ClientID, -1)
99+
parsed = strings.Replace(parsed, "{{clientSecret}}", conf.ClientSecret, -1)
100+
parsed = strings.Replace(parsed, "{{appObjectID}}", conf.AppObjectID, -1)
101+
parsed = strings.Replace(parsed, "{{maxCredValidationSeconds}}", strconv.Itoa(maxSecs), -1)
102+
return parsed
103+
}
104+
41105
func testAccDataSourceAzureAccessCredentialsConfigBasic(mountPath string, conf *testutil.AzureTestConf, maxSecs int) string {
42106
template := `
43107
resource "vault_azure_secret_backend" "test" {

0 commit comments

Comments
 (0)