Skip to content

Commit 90e3ab0

Browse files
authored
Merge pull request #28215 from pureiboi/b-aws_appstream_image_builder-26677
aws_appstream_image_builder: domain_join_info and vpc_config throw error #26677
2 parents d01a0d0 + 4727ec6 commit 90e3ab0

File tree

6 files changed

+188
-125
lines changed

6 files changed

+188
-125
lines changed

.changelog/26677.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```release-note:bug
2+
resource/aws_appstream_image_builder: Fix IAM eventual consistency error for optional role
3+
```
4+
5+
```release-note:bug
6+
resource/aws_appstream_image_builder: Fix refresh error when `domain_join_info` and `vpc_config` are not empty
7+
```

internal/service/appstream/find.go

+40-16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/aws/aws-sdk-go/service/appstream"
99
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
1010
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
11+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
1112
)
1213

1314
// FindStackByName Retrieve a appstream stack by name
@@ -58,26 +59,38 @@ func FindFleetByName(ctx context.Context, conn *appstream.AppStream, name string
5859
return fleet, nil
5960
}
6061

61-
// FindImageBuilderByName Retrieve a appstream ImageBuilder by name
6262
func FindImageBuilderByName(ctx context.Context, conn *appstream.AppStream, name string) (*appstream.ImageBuilder, error) {
6363
input := &appstream.DescribeImageBuildersInput{
64-
Names: []*string{aws.String(name)},
64+
Names: aws.StringSlice([]string{name}),
65+
}
66+
67+
output, err := findImageBuilder(ctx, conn, input)
68+
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
// Eventual consistency check.
74+
if aws.StringValue(output.Name) != name {
75+
return nil, &resource.NotFoundError{
76+
LastRequest: input,
77+
}
6578
}
6679

67-
var result *appstream.ImageBuilder
80+
return output, nil
81+
}
82+
83+
func findImageBuilders(ctx context.Context, conn *appstream.AppStream, input *appstream.DescribeImageBuildersInput) ([]*appstream.ImageBuilder, error) {
84+
var output []*appstream.ImageBuilder
6885

6986
err := describeImageBuildersPagesWithContext(ctx, conn, input, func(page *appstream.DescribeImageBuildersOutput, lastPage bool) bool {
7087
if page == nil {
7188
return !lastPage
7289
}
7390

74-
for _, imageBuilder := range page.ImageBuilders {
75-
if imageBuilder == nil {
76-
continue
77-
}
78-
if aws.StringValue(imageBuilder.Name) == name {
79-
result = imageBuilder
80-
return false
91+
for _, v := range page.ImageBuilders {
92+
if v != nil {
93+
output = append(output, v)
8194
}
8295
}
8396

@@ -95,14 +108,25 @@ func FindImageBuilderByName(ctx context.Context, conn *appstream.AppStream, name
95108
return nil, err
96109
}
97110

98-
if result == nil {
99-
return nil, &resource.NotFoundError{
100-
Message: "Empty result",
101-
LastRequest: input,
102-
}
111+
return output, nil
112+
}
113+
114+
func findImageBuilder(ctx context.Context, conn *appstream.AppStream, input *appstream.DescribeImageBuildersInput) (*appstream.ImageBuilder, error) {
115+
output, err := findImageBuilders(ctx, conn, input)
116+
117+
if err != nil {
118+
return nil, err
103119
}
104120

105-
return result, nil
121+
if len(output) == 0 || output[0] == nil {
122+
return nil, tfresource.NewEmptyResultError(input)
123+
}
124+
125+
if count := len(output); count > 1 {
126+
return nil, tfresource.NewTooManyResultsError(count, input)
127+
}
128+
129+
return output[0], nil
106130
}
107131

108132
// FindUserByUserNameAndAuthType Retrieve a appstream fleet by Username and authentication type

internal/service/appstream/image_builder.go

+54-53
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package appstream
22

33
import (
44
"context"
5-
"fmt"
65
"log"
76
"time"
87

@@ -15,6 +14,7 @@ import (
1514
"github.com/hashicorp/terraform-provider-aws/internal/conns"
1615
"github.com/hashicorp/terraform-provider-aws/internal/flex"
1716
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
17+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
1818
"github.com/hashicorp/terraform-provider-aws/internal/verify"
1919
)
2020

@@ -24,9 +24,11 @@ func ResourceImageBuilder() *schema.Resource {
2424
ReadWithoutTimeout: resourceImageBuilderRead,
2525
UpdateWithoutTimeout: resourceImageBuilderUpdate,
2626
DeleteWithoutTimeout: resourceImageBuilderDelete,
27+
2728
Importer: &schema.ResourceImporter{
2829
StateContext: schema.ImportStatePassthroughContext,
2930
},
31+
3032
Schema: map[string]*schema.Schema{
3133
"access_endpoint": {
3234
Type: schema.TypeSet,
@@ -139,6 +141,8 @@ func ResourceImageBuilder() *schema.Resource {
139141
Type: schema.TypeString,
140142
Computed: true,
141143
},
144+
"tags": tftags.TagsSchema(),
145+
"tags_all": tftags.TagsSchemaComputed(),
142146
"vpc_config": {
143147
Type: schema.TypeList,
144148
MaxItems: 1,
@@ -162,9 +166,8 @@ func ResourceImageBuilder() *schema.Resource {
162166
},
163167
},
164168
},
165-
"tags": tftags.TagsSchema(),
166-
"tags_all": tftags.TagsSchemaComputed(),
167169
},
170+
168171
CustomizeDiff: verify.SetTagsDiff,
169172
}
170173
}
@@ -175,24 +178,23 @@ func resourceImageBuilderCreate(ctx context.Context, d *schema.ResourceData, met
175178
tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{})))
176179

177180
name := d.Get("name").(string)
178-
179181
input := &appstream.CreateImageBuilderInput{
180-
Name: aws.String(name),
181182
InstanceType: aws.String(d.Get("instance_type").(string)),
183+
Name: aws.String(name),
182184
}
183185

184186
if v, ok := d.GetOk("access_endpoint"); ok && v.(*schema.Set).Len() > 0 {
185187
input.AccessEndpoints = expandAccessEndpoints(v.(*schema.Set).List())
186188
}
187189

188-
if v, ok := d.GetOk("description"); ok {
189-
input.Description = aws.String(v.(string))
190-
}
191-
192190
if v, ok := d.GetOk("appstream_agent_version"); ok {
193191
input.AppstreamAgentVersion = aws.String(v.(string))
194192
}
195193

194+
if v, ok := d.GetOk("description"); ok {
195+
input.Description = aws.String(v.(string))
196+
}
197+
196198
if v, ok := d.GetOk("display_name"); ok {
197199
input.DisplayName = aws.String(v.(string))
198200
}
@@ -205,6 +207,10 @@ func resourceImageBuilderCreate(ctx context.Context, d *schema.ResourceData, met
205207
input.EnableDefaultInternetAccess = aws.Bool(v.(bool))
206208
}
207209

210+
if v, ok := d.GetOk("iam_role_arn"); ok {
211+
input.IamRoleArn = aws.String(v.(string))
212+
}
213+
208214
if v, ok := d.GetOk("image_arn"); ok {
209215
input.ImageArn = aws.String(v.(string))
210216
}
@@ -213,10 +219,6 @@ func resourceImageBuilderCreate(ctx context.Context, d *schema.ResourceData, met
213219
input.ImageName = aws.String(v.(string))
214220
}
215221

216-
if v, ok := d.GetOk("iam_role_arn"); ok {
217-
input.IamRoleArn = aws.String(v.(string))
218-
}
219-
220222
if v, ok := d.GetOk("vpc_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
221223
input.VpcConfig = expandImageBuilderVPCConfig(v.([]interface{}))
222224
}
@@ -225,96 +227,98 @@ func resourceImageBuilderCreate(ctx context.Context, d *schema.ResourceData, met
225227
input.Tags = Tags(tags.IgnoreAWS())
226228
}
227229

228-
output, err := conn.CreateImageBuilderWithContext(ctx, input)
230+
outputRaw, err := tfresource.RetryWhenAWSErrMessageContainsContext(ctx, iamPropagationTimeout, func() (interface{}, error) {
231+
return conn.CreateImageBuilderWithContext(ctx, input)
232+
}, appstream.ErrCodeInvalidRoleException, "encountered an error because your IAM role")
229233

230234
if err != nil {
231-
return diag.FromErr(fmt.Errorf("error creating Appstream ImageBuilder (%s): %w", name, err))
235+
return diag.Errorf("creating AppStream ImageBuilder (%s): %s", name, err)
232236
}
233237

234-
d.SetId(aws.StringValue(output.ImageBuilder.Name))
238+
d.SetId(aws.StringValue(outputRaw.(*appstream.CreateImageBuilderOutput).ImageBuilder.Name))
235239

236240
if _, err = waitImageBuilderStateRunning(ctx, conn, d.Id()); err != nil {
237-
return diag.FromErr(fmt.Errorf("error waiting for Appstream ImageBuilder (%s) to be running: %w", d.Id(), err))
241+
return diag.Errorf("waiting for AppStream ImageBuilder (%s) create: %s", d.Id(), err)
238242
}
239243

240244
return resourceImageBuilderRead(ctx, d, meta)
241245
}
242246

243247
func resourceImageBuilderRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
244248
conn := meta.(*conns.AWSClient).AppStreamConn()
245-
246249
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
247250
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig
248251

249252
imageBuilder, err := FindImageBuilderByName(ctx, conn, d.Id())
250253

251-
if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) {
252-
log.Printf("[WARN] Appstream ImageBuilder (%s) not found, removing from state", d.Id())
254+
if !d.IsNewResource() && tfresource.NotFound(err) {
255+
log.Printf("[WARN] AppStream ImageBuilder (%s) not found, removing from state", d.Id())
253256
d.SetId("")
254257
return nil
255258
}
256259

257260
if err != nil {
258-
return diag.FromErr(fmt.Errorf("error reading Appstream ImageBuilder (%s): %w", d.Id(), err))
261+
return diag.Errorf("reading AppStream ImageBuilder (%s): %s", d.Id(), err)
259262
}
260263

261-
if imageBuilder == nil {
262-
return diag.FromErr(fmt.Errorf("error reading Appstream ImageBuilder (%s): not found after creation", d.Id()))
264+
if err = d.Set("access_endpoint", flattenAccessEndpoints(imageBuilder.AccessEndpoints)); err != nil {
265+
return diag.Errorf("setting access_endpoint: %s", err)
263266
}
264-
265-
arn := aws.StringValue(imageBuilder.Arn)
266-
267267
d.Set("appstream_agent_version", imageBuilder.AppstreamAgentVersion)
268+
arn := aws.StringValue(imageBuilder.Arn)
268269
d.Set("arn", arn)
269270
d.Set("created_time", aws.TimeValue(imageBuilder.CreatedTime).Format(time.RFC3339))
270271
d.Set("description", imageBuilder.Description)
271272
d.Set("display_name", imageBuilder.DisplayName)
273+
if imageBuilder.DomainJoinInfo != nil {
274+
if err = d.Set("domain_join_info", []interface{}{flattenDomainInfo(imageBuilder.DomainJoinInfo)}); err != nil {
275+
return diag.Errorf("setting domain_join_info: %s", err)
276+
}
277+
} else {
278+
d.Set("domain_join_info", nil)
279+
}
272280
d.Set("enable_default_internet_access", imageBuilder.EnableDefaultInternetAccess)
273-
d.Set("image_arn", imageBuilder.ImageArn)
274281
d.Set("iam_role_arn", imageBuilder.IamRoleArn)
282+
d.Set("image_arn", imageBuilder.ImageArn)
275283
d.Set("instance_type", imageBuilder.InstanceType)
276-
277-
if err = d.Set("access_endpoint", flattenAccessEndpoints(imageBuilder.AccessEndpoints)); err != nil {
278-
return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream ImageBuilder (%s): %w", "access_endpoints", d.Id(), err))
279-
}
280-
if err = d.Set("domain_join_info", flattenDomainInfo(imageBuilder.DomainJoinInfo)); err != nil {
281-
return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream ImageBuilder (%s): %w", "domain_join_info", d.Id(), err))
282-
}
283-
284-
if err = d.Set("vpc_config", flattenVPCConfig(imageBuilder.VpcConfig)); err != nil {
285-
return diag.FromErr(fmt.Errorf("error setting `%s` for AppStream ImageBuilder (%s): %w", "vpc_config", d.Id(), err))
286-
}
287-
288284
d.Set("name", imageBuilder.Name)
289285
d.Set("state", imageBuilder.State)
286+
if imageBuilder.VpcConfig != nil {
287+
if err = d.Set("vpc_config", []interface{}{flattenVPCConfig(imageBuilder.VpcConfig)}); err != nil {
288+
return diag.Errorf("setting vpc_config: %s", err)
289+
}
290+
} else {
291+
d.Set("vpc_config", nil)
292+
}
293+
294+
tags, err := ListTagsWithContext(ctx, conn, arn)
290295

291-
tags, err := ListTags(conn, arn)
292296
if err != nil {
293-
return diag.FromErr(fmt.Errorf("error listing tags for AppStream ImageBuilder (%s): %w", arn, err))
297+
return diag.Errorf("listing tags for AppStream ImageBuilder (%s): %s", arn, err)
294298
}
295299

296300
tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
297301

298302
//lintignore:AWSR002
299303
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil {
300-
return diag.FromErr(fmt.Errorf("error setting tags: %w", err))
304+
return diag.Errorf("setting tags: %s", err)
301305
}
302306

303307
if err := d.Set("tags_all", tags.Map()); err != nil {
304-
return diag.FromErr(fmt.Errorf("error setting tags_all: %w", err))
308+
return diag.Errorf("setting tags_all: %s", err)
305309
}
306310

307311
return nil
308312
}
309313

310314
func resourceImageBuilderUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
311-
if d.HasChange("tags_all") {
312-
conn := meta.(*conns.AWSClient).AppStreamConn()
315+
conn := meta.(*conns.AWSClient).AppStreamConn()
313316

317+
if d.HasChange("tags_all") {
314318
o, n := d.GetChange("tags_all")
315319

316-
if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
317-
return diag.FromErr(fmt.Errorf("error updating tags for AppStream ImageBuilder (%s): %w", d.Id(), err))
320+
if err := UpdateTagsWithContext(ctx, conn, d.Get("arn").(string), o, n); err != nil {
321+
return diag.Errorf("updating tags for AppStream ImageBuilder (%s): %s", d.Id(), err)
318322
}
319323
}
320324

@@ -324,7 +328,7 @@ func resourceImageBuilderUpdate(ctx context.Context, d *schema.ResourceData, met
324328
func resourceImageBuilderDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
325329
conn := meta.(*conns.AWSClient).AppStreamConn()
326330

327-
log.Printf("[DEBUG] Deleting AppStream Image Builder: (%s)", d.Id())
331+
log.Printf("[DEBUG] Deleting AppStream ImageBuilder: %s", d.Id())
328332
_, err := conn.DeleteImageBuilderWithContext(ctx, &appstream.DeleteImageBuilderInput{
329333
Name: aws.String(d.Id()),
330334
})
@@ -334,14 +338,11 @@ func resourceImageBuilderDelete(ctx context.Context, d *schema.ResourceData, met
334338
}
335339

336340
if err != nil {
337-
return diag.FromErr(fmt.Errorf("error deleting Appstream ImageBuilder (%s): %w", d.Id(), err))
341+
return diag.Errorf("deleting AppStream ImageBuilder (%s): %s", d.Id(), err)
338342
}
339343

340344
if _, err = waitImageBuilderStateDeleted(ctx, conn, d.Id()); err != nil {
341-
if tfawserr.ErrCodeEquals(err, appstream.ErrCodeResourceNotFoundException) {
342-
return nil
343-
}
344-
return diag.FromErr(fmt.Errorf("error waiting for Appstream ImageBuilder (%s) to be deleted: %w", d.Id(), err))
345+
return diag.Errorf("waiting for AppStream ImageBuilder (%s) delete: %s", d.Id(), err)
345346
}
346347

347348
return nil

0 commit comments

Comments
 (0)