@@ -17,6 +17,7 @@ import (
1717 "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
1818 "github.com/hashicorp/terraform-plugin-framework/types"
1919 "github.com/hashicorp/terraform-plugin-log/tflog"
20+ vaultapi "github.com/hashicorp/vault/api"
2021
2122 "github.com/hashicorp/terraform-provider-vault/internal/consts"
2223 "github.com/hashicorp/terraform-provider-vault/internal/framework/base"
@@ -108,17 +109,12 @@ func (r *DistributeKeyResource) Create(ctx context.Context, req resource.CreateR
108109 return
109110 }
110111
111- cli , err := client .GetClient (ctx , r .Meta (), data .Namespace .ValueString ())
112- if err != nil {
113- resp .Diagnostics .AddError (errutil .ClientConfigureErr (err ))
112+ cli , ok := r .getVaultClient (ctx , data .Namespace .ValueString (), & resp .Diagnostics )
113+ if ! ok {
114114 return
115115 }
116116
117- vaultPath := data .Mount .ValueString ()
118- kmsName := data .KMSName .ValueString ()
119- keyName := data .KeyName .ValueString ()
120- apiPath := BuildDistributeKeyPath (vaultPath , kmsName , keyName )
121-
117+ apiPath := data .APIPath ()
122118 writeData := map [string ]interface {}{}
123119
124120 var purposes []string
@@ -132,23 +128,27 @@ func (r *DistributeKeyResource) Create(ctx context.Context, req resource.CreateR
132128 writeData ["protection" ] = data .Protection .ValueString ()
133129 }
134130
135- writeResp , err := cli .Logical ().WriteWithContext (ctx , apiPath , writeData )
136- if err != nil {
131+ if _ , err := cli .Logical ().WriteWithContext (ctx , apiPath , writeData ); err != nil {
137132 resp .Diagnostics .AddError (ErrCreating (ResourceTypeKeyDistribution , apiPath , err ))
138133 return
139134 }
140135
141- // Parse the write response to extract all fields including computed ones
142- if writeResp != nil && writeResp .Data != nil {
143- parseDistributeKeyResponse (ctx , writeResp .Data , & data , & resp .Diagnostics )
144- if resp .Diagnostics .HasError () {
145- return
146- }
147- } else {
148- // Ensure computed field is set to a known value even if write response is empty
149- if data .Versions .IsUnknown () {
150- data .Versions , _ = types .MapValueFrom (ctx , types .StringType , map [string ]string {})
151- }
136+ // Read back the state from Vault
137+ responseData , exists := r .readDistributeKey (ctx , cli , apiPath , & resp .Diagnostics )
138+ if resp .Diagnostics .HasError () {
139+ return
140+ }
141+ if ! exists {
142+ resp .Diagnostics .AddError (
143+ "Unexpected error after creating key distribution" ,
144+ fmt .Sprintf ("Key distribution not found at path %q immediately after creation" , apiPath ),
145+ )
146+ return
147+ }
148+
149+ data .parseDistributeKeyResponse (ctx , responseData , & resp .Diagnostics )
150+ if resp .Diagnostics .HasError () {
151+ return
152152 }
153153
154154 resp .Diagnostics .Append (resp .State .Set (ctx , & data )... )
@@ -161,21 +161,17 @@ func (r *DistributeKeyResource) Read(ctx context.Context, req resource.ReadReque
161161 return
162162 }
163163
164- cli , err := client .GetClient (ctx , r .Meta (), data .Namespace .ValueString ())
165- if err != nil {
166- resp .Diagnostics .AddError (errutil .ClientConfigureErr (err ))
164+ cli , ok := r .getVaultClient (ctx , data .Namespace .ValueString (), & resp .Diagnostics )
165+ if ! ok {
167166 return
168167 }
169168
170- // Build API path from data fields
171- apiPath := BuildDistributeKeyPath (data .Mount .ValueString (), data .KMSName .ValueString (), data .KeyName .ValueString ())
172- vaultResp , err := cli .Logical ().ReadWithContext (ctx , apiPath )
173- if err != nil {
174- resp .Diagnostics .AddError (ErrReading (ResourceTypeKeyDistribution , apiPath , err ))
169+ apiPath := data .APIPath ()
170+ responseData , exists := r .readDistributeKey (ctx , cli , apiPath , & resp .Diagnostics )
171+ if resp .Diagnostics .HasError () {
175172 return
176173 }
177-
178- if vaultResp == nil {
174+ if ! exists {
179175 tflog .Warn (ctx , "key distribution not found, removing from state" , map [string ]interface {}{
180176 "path" : apiPath ,
181177 })
@@ -184,7 +180,7 @@ func (r *DistributeKeyResource) Read(ctx context.Context, req resource.ReadReque
184180 }
185181
186182 // Parse response data
187- parseDistributeKeyResponse (ctx , vaultResp . Data , & data , & resp .Diagnostics )
183+ data . parseDistributeKeyResponse (ctx , responseData , & resp .Diagnostics )
188184 if resp .Diagnostics .HasError () {
189185 return
190186 }
@@ -200,14 +196,12 @@ func (r *DistributeKeyResource) Update(ctx context.Context, req resource.UpdateR
200196 return
201197 }
202198
203- cli , err := client .GetClient (ctx , r .Meta (), plan .Namespace .ValueString ())
204- if err != nil {
205- resp .Diagnostics .AddError (errutil .ClientConfigureErr (err ))
199+ cli , ok := r .getVaultClient (ctx , plan .Namespace .ValueString (), & resp .Diagnostics )
200+ if ! ok {
206201 return
207202 }
208203
209- // Build API path from fields
210- apiPath := BuildDistributeKeyPath (plan .Mount .ValueString (), plan .KMSName .ValueString (), plan .KeyName .ValueString ())
204+ apiPath := plan .APIPath ()
211205 writeData := map [string ]interface {}{}
212206 hasChanges := false
213207
@@ -228,22 +222,28 @@ func (r *DistributeKeyResource) Update(ctx context.Context, req resource.UpdateR
228222 }
229223
230224 if hasChanges {
231- writeResp , err := cli .Logical ().WriteWithContext (ctx , apiPath , writeData )
232- if err != nil {
225+ if _ , err := cli .Logical ().WriteWithContext (ctx , apiPath , writeData ); err != nil {
233226 resp .Diagnostics .AddError (ErrUpdating (ResourceTypeKeyDistribution , apiPath , err ))
234227 return
235228 }
229+ }
236230
237- // Parse the write response to extract updated fields
238- if writeResp != nil && writeResp .Data != nil {
239- parseDistributeKeyResponse (ctx , writeResp .Data , & plan , & resp .Diagnostics )
240- if resp .Diagnostics .HasError () {
241- return
242- }
243- }
244- } else {
245- // No changes, preserve existing computed values
246- plan .Versions = state .Versions
231+ // Read back the state from Vault
232+ responseData , exists := r .readDistributeKey (ctx , cli , apiPath , & resp .Diagnostics )
233+ if resp .Diagnostics .HasError () {
234+ return
235+ }
236+ if ! exists {
237+ resp .Diagnostics .AddError (
238+ "Unexpected error after updating key distribution" ,
239+ fmt .Sprintf ("Key distribution not found at path %q immediately after update" , apiPath ),
240+ )
241+ return
242+ }
243+
244+ plan .parseDistributeKeyResponse (ctx , responseData , & resp .Diagnostics )
245+ if resp .Diagnostics .HasError () {
246+ return
247247 }
248248
249249 resp .Diagnostics .Append (resp .State .Set (ctx , & plan )... )
@@ -256,14 +256,12 @@ func (r *DistributeKeyResource) Delete(ctx context.Context, req resource.DeleteR
256256 return
257257 }
258258
259- cli , err := client .GetClient (ctx , r .Meta (), data .Namespace .ValueString ())
260- if err != nil {
261- resp .Diagnostics .AddError (errutil .ClientConfigureErr (err ))
259+ cli , ok := r .getVaultClient (ctx , data .Namespace .ValueString (), & resp .Diagnostics )
260+ if ! ok {
262261 return
263262 }
264263
265- // Build API path from fields
266- apiPath := BuildDistributeKeyPath (data .Mount .ValueString (), data .KMSName .ValueString (), data .KeyName .ValueString ())
264+ apiPath := data .APIPath ()
267265 if _ , err := cli .Logical ().DeleteWithContext (ctx , apiPath ); err != nil {
268266 resp .Diagnostics .AddError (ErrDeleting (ResourceTypeKeyDistribution , apiPath , err ))
269267 return
@@ -311,9 +309,36 @@ func (r *DistributeKeyResource) ImportState(ctx context.Context, req resource.Im
311309 }
312310}
313311
314- // parseDistributeKeyResponse extracts data from Vault API response data into the distribute key model
315- // This works with both write responses and read responses from the key distribution API
316- func parseDistributeKeyResponse (ctx context.Context , responseData map [string ]interface {}, data * DistributeKeyResourceModel , diags * diag.Diagnostics ) {
312+ // APIPath returns the Vault API path for this key distribution.
313+ func (m * DistributeKeyResourceModel ) APIPath () string {
314+ return BuildDistributeKeyPath (m .Mount .ValueString (), m .KMSName .ValueString (), m .KeyName .ValueString ())
315+ }
316+
317+ // getVaultClient returns a Vault client for the given namespace, adding a diagnostic on error.
318+ func (r * DistributeKeyResource ) getVaultClient (ctx context.Context , namespace string , diags * diag.Diagnostics ) (* vaultapi.Client , bool ) {
319+ cli , err := client .GetClient (ctx , r .Meta (), namespace )
320+ if err != nil {
321+ diags .AddError (errutil .ClientConfigureErr (err ))
322+ return nil , false
323+ }
324+ return cli , true
325+ }
326+
327+ // readDistributeKey reads the key distribution from Vault. Returns (data, true) if found, (nil, false) otherwise. API errors are added to diags.
328+ func (r * DistributeKeyResource ) readDistributeKey (ctx context.Context , cli * vaultapi.Client , apiPath string , diags * diag.Diagnostics ) (map [string ]interface {}, bool ) {
329+ vaultResp , err := cli .Logical ().ReadWithContext (ctx , apiPath )
330+ if err != nil {
331+ diags .AddError (ErrReading (ResourceTypeKeyDistribution , apiPath , err ))
332+ return nil , false
333+ }
334+ if vaultResp == nil {
335+ return nil , false
336+ }
337+ return vaultResp .Data , true
338+ }
339+
340+ // parseDistributeKeyResponse extracts data from Vault API response data into the distribute key model.
341+ func (data * DistributeKeyResourceModel ) parseDistributeKeyResponse (ctx context.Context , responseData map [string ]interface {}, diags * diag.Diagnostics ) {
317342 if v , ok := responseData ["purpose" ].([]interface {}); ok {
318343 purposes , d := types .SetValueFrom (ctx , types .StringType , v )
319344 diags .Append (d ... )
0 commit comments