Skip to content

Commit c264899

Browse files
Resolve TF state for PKI Multi-Issuer workflows (hashicorp#1973)
1 parent adac680 commit c264899

8 files changed

+321
-52
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
github.com/denisenkom/go-mssqldb v0.12.0
2121
github.com/docker/distribution v2.8.1+incompatible // indirect
2222
github.com/go-sql-driver/mysql v1.6.0
23+
github.com/google/uuid v1.3.0
2324
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
2425
github.com/gosimple/slug v1.11.0
2526
github.com/hashicorp/errwrap v1.1.0

util/util.go

+8
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ func ToStringArray(input []interface{}) []string {
5151
return output
5252
}
5353

54+
func Is500(err error) bool {
55+
return ErrorContainsHTTPCode(err, http.StatusInternalServerError)
56+
}
57+
5458
func Is404(err error) bool {
5559
return ErrorContainsHTTPCode(err, http.StatusNotFound)
5660
}
@@ -64,6 +68,10 @@ func ErrorContainsHTTPCode(err error, codes ...int) bool {
6468
return false
6569
}
6670

71+
func ErrorContainsString(err error, s string) bool {
72+
return strings.Contains(err.Error(), s)
73+
}
74+
6775
// CalculateConflictsWith returns a slice of field names that conflict with
6876
// a single field (self).
6977
func CalculateConflictsWith(self string, group []string) []string {

vault/resource_pki_secret_backend_intermediate_cert_request.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ package vault
55

66
import (
77
"context"
8+
"fmt"
89
"log"
910
"strings"
1011

12+
"github.com/google/uuid"
1113
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1214
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1315
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
@@ -231,7 +233,8 @@ func pkiSecretBackendIntermediateCertRequestCreate(ctx context.Context, d *schem
231233

232234
backend := d.Get(consts.FieldBackend).(string)
233235
intermediateType := d.Get(consts.FieldType).(string)
234-
path := pkiSecretBackendIntermediateGeneratePath(backend, intermediateType)
236+
237+
path := pkiSecretBackendIntermediateGeneratePath(backend, intermediateType, provider.IsAPISupported(meta, provider.VaultVersion111))
235238

236239
intermediateCertAPIFields := []string{
237240
consts.FieldCommonName,
@@ -263,14 +266,18 @@ func pkiSecretBackendIntermediateCertRequestCreate(ctx context.Context, d *schem
263266
// add multi-issuer write API fields if supported
264267
isIssuerAPISupported := provider.IsAPISupported(meta, provider.VaultVersion111)
265268

269+
// Fields only used when we are generating a key
266270
if !(intermediateType == keyTypeKMS || intermediateType == consts.FieldExisting) {
267-
// if kms or existing type,
268271
intermediateCertAPIFields = append(intermediateCertAPIFields, consts.FieldKeyType, consts.FieldKeyBits)
269-
if isIssuerAPISupported {
272+
}
273+
274+
if isIssuerAPISupported {
275+
// Note: CSR generation does not persist an issuer, just a key, so consts.FieldIssuerName is not supported
276+
if intermediateType == consts.FieldExisting {
270277
intermediateCertAPIFields = append(intermediateCertAPIFields, consts.FieldKeyRef)
278+
} else {
279+
intermediateCertAPIFields = append(intermediateCertAPIFields, consts.FieldKeyName)
271280
}
272-
} else if isIssuerAPISupported {
273-
intermediateCertAPIFields = append(intermediateCertAPIFields, consts.FieldKeyName)
274281
}
275282

276283
data := map[string]interface{}{}
@@ -329,7 +336,15 @@ func pkiSecretBackendIntermediateCertRequestCreate(ctx context.Context, d *schem
329336

330337
}
331338

332-
d.SetId(path)
339+
id := path
340+
if provider.IsAPISupported(meta, provider.VaultVersion111) {
341+
// multiple CSRs can be generated
342+
// ensure unique IDs
343+
uniqueSuffix := uuid.New()
344+
id = fmt.Sprintf("%s/%s", path, uniqueSuffix)
345+
}
346+
347+
d.SetId(id)
333348
return pkiSecretBackendIntermediateCertRequestRead(ctx, d, meta)
334349
}
335350

@@ -341,6 +356,9 @@ func pkiSecretBackendIntermediateCertRequestDelete(ctx context.Context, d *schem
341356
return nil
342357
}
343358

344-
func pkiSecretBackendIntermediateGeneratePath(backend string, intermediateType string) string {
359+
func pkiSecretBackendIntermediateGeneratePath(backend, intermediateType string, isMultiIssuerSupported bool) string {
360+
if isMultiIssuerSupported {
361+
return strings.Trim(backend, "/") + "/issuers/generate/intermediate/" + strings.Trim(intermediateType, "/")
362+
}
345363
return strings.Trim(backend, "/") + "/intermediate/generate/" + strings.Trim(intermediateType, "/")
346364
}

vault/resource_pki_secret_backend_intermediate_cert_request_test.go

+60-13
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,27 @@ func TestPkiSecretBackendIntermediateCertificate_multiIssuer(t *testing.T) {
8080
resourceName := "vault_pki_secret_backend_intermediate_cert_request.test"
8181
keyName := acctest.RandomWithPrefix("test-pki-key")
8282

83-
checks := []resource.TestCheckFunc{
83+
// used to test existing key flow
84+
store := &testPKIKeyStore{}
85+
keyResourceName := "vault_pki_secret_backend_key.test"
86+
updatedKeyName := acctest.RandomWithPrefix("test-pki-key-updated")
87+
88+
commonChecks := []resource.TestCheckFunc{
8489
resource.TestCheckResourceAttr(resourceName, consts.FieldBackend, path),
85-
resource.TestCheckResourceAttr(resourceName, consts.FieldType, "internal"),
90+
resource.TestCheckResourceAttrSet(resourceName, consts.FieldKeyID),
8691
resource.TestCheckResourceAttr(resourceName, consts.FieldCommonName, "test Intermediate CA"),
92+
}
93+
94+
internalChecks := append(commonChecks,
95+
resource.TestCheckResourceAttr(resourceName, consts.FieldType, "internal"),
96+
// keyName is only set on internal if it is passed by user
8797
resource.TestCheckResourceAttr(resourceName, consts.FieldKeyName, keyName),
88-
resource.TestCheckResourceAttrSet(resourceName, consts.FieldKeyID),
98+
)
99+
100+
existingChecks := append(commonChecks,
101+
resource.TestCheckResourceAttr(resourceName, consts.FieldType, "existing"),
89102
resource.TestCheckResourceAttrSet(resourceName, consts.FieldKeyRef),
90-
}
103+
)
91104

92105
resource.Test(t, resource.TestCase{
93106
ProviderFactories: providerFactories,
@@ -97,11 +110,27 @@ func TestPkiSecretBackendIntermediateCertificate_multiIssuer(t *testing.T) {
97110
},
98111
CheckDestroy: testCheckMountDestroyed("vault_mount", consts.MountTypePKI, consts.FieldPath),
99112
Steps: []resource.TestStep{
100-
// @TODO add a test step with a key_ref
101113
{
102-
Config: testPkiSecretBackendIntermediateCertRequestConfig_multiIssuer(path, keyName),
114+
Config: testPkiSecretBackendIntermediateCertRequestConfig_multiIssuerInternal(path, keyName),
103115
Check: resource.ComposeTestCheckFunc(
104-
append(checks)...,
116+
append(internalChecks)...,
117+
),
118+
},
119+
{
120+
// Create and capture key ID
121+
Config: testAccPKISecretBackendKey_basic(path, updatedKeyName, "rsa", "2048"),
122+
Check: resource.ComposeTestCheckFunc(
123+
testCapturePKIKeyID(keyResourceName, store),
124+
),
125+
},
126+
{
127+
Config: testPkiSecretBackendIntermediateCertRequestConfig_multiIssuerExisting(path, updatedKeyName),
128+
Check: resource.ComposeTestCheckFunc(
129+
append(existingChecks,
130+
// confirm that root cert key ID is same as the key
131+
// created in step 2; thereby confirming key_ref is passed
132+
testPKIKeyUpdate(resourceName, store, true),
133+
)...,
105134
),
106135
},
107136
},
@@ -128,7 +157,26 @@ resource "vault_pki_secret_backend_intermediate_cert_request" "test" {
128157
`, path, addConstraints)
129158
}
130159

131-
func testPkiSecretBackendIntermediateCertRequestConfig_multiIssuer(path, keyName string) string {
160+
func testPkiSecretBackendIntermediateCertRequestConfig_multiIssuerInternal(path, keyName string) string {
161+
return fmt.Sprintf(`
162+
resource "vault_mount" "test" {
163+
path = "%s"
164+
type = "pki"
165+
description = "test"
166+
default_lease_ttl_seconds = 86400
167+
max_lease_ttl_seconds = 86400
168+
}
169+
170+
resource "vault_pki_secret_backend_intermediate_cert_request" "test" {
171+
backend = vault_mount.test.path
172+
type = "internal"
173+
common_name = "test Intermediate CA"
174+
key_name = "%s"
175+
}
176+
`, path, keyName)
177+
}
178+
179+
func testPkiSecretBackendIntermediateCertRequestConfig_multiIssuerExisting(path, keyName string) string {
132180
return fmt.Sprintf(`
133181
resource "vault_mount" "test" {
134182
path = "%s"
@@ -141,17 +189,16 @@ resource "vault_mount" "test" {
141189
resource "vault_pki_secret_backend_key" "test" {
142190
backend = vault_mount.test.path
143191
type = "exported"
144-
key_name = "test"
192+
key_name = "%s"
145193
key_type = "rsa"
146-
key_bits = "4096"
194+
key_bits = "2048"
147195
}
148196
149197
resource "vault_pki_secret_backend_intermediate_cert_request" "test" {
150198
backend = vault_mount.test.path
151-
type = "internal"
199+
type = "existing"
152200
common_name = "test Intermediate CA"
153-
key_ref = vault_pki_secret_backend_key.test.id
154-
key_name = "%s"
201+
key_ref = vault_pki_secret_backend_key.test.key_id
155202
}
156203
`, path, keyName)
157204
}

vault/resource_pki_secret_backend_issuer.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ func pkiSecretBackendIssuerRead(ctx context.Context, d *schema.ResourceData, met
205205

206206
log.Printf("[DEBUG] Reading %s from Vault", path)
207207
resp, err := client.Logical().ReadWithContext(ctx, path)
208+
if resp == nil {
209+
d.SetId("")
210+
return nil
211+
}
212+
208213
if err != nil {
209214
return diag.Errorf("error reading from Vault: %s", err)
210215
}
@@ -232,9 +237,11 @@ func pkiSecretBackendIssuerRead(ctx context.Context, d *schema.ResourceData, met
232237
}
233238

234239
for _, k := range fields {
235-
if err := d.Set(k, resp.Data[k]); err != nil {
236-
return diag.Errorf("error setting state key %q for issuer, err=%s",
237-
k, err)
240+
if v, ok := resp.Data[k]; ok {
241+
if err := d.Set(k, v); err != nil {
242+
return diag.Errorf("error setting state key %q for issuer, err=%s",
243+
k, err)
244+
}
238245
}
239246
}
240247

vault/resource_pki_secret_backend_key_test.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,16 @@ func TestAccPKISecretBackendKey_basic(t *testing.T) {
8686

8787
func testAccPKISecretBackendKey_basic(path, keyName, keyType, keyBits string) string {
8888
return fmt.Sprintf(`
89-
resource "vault_mount" "pki" {
90-
path = "%s"
91-
type = "pki"
92-
description = "PKI secret engine mount"
89+
resource "vault_mount" "test" {
90+
path = "%s"
91+
type = "pki"
92+
description = "test"
93+
default_lease_ttl_seconds = "86400"
94+
max_lease_ttl_seconds = "86400"
9395
}
9496
9597
resource "vault_pki_secret_backend_key" "test" {
96-
backend = vault_mount.pki.path
98+
backend = vault_mount.test.path
9799
type = "exported"
98100
key_name = "%s"
99101
key_type = "%s"

0 commit comments

Comments
 (0)