Skip to content

Commit 95a31f7

Browse files
authored
Merge pull request #47651 from MarshallJones92/f-securityhub_v2_aggregator
New Resource: aws_securityhub_aggregator_v2
2 parents 51db0c1 + f3990c5 commit 95a31f7

11 files changed

Lines changed: 953 additions & 0 deletions

File tree

.changelog/47651.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-resource
2+
aws_securityhub_aggregator_v2
3+
```
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
// Copyright IBM Corp. 2014, 2026
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package securityhub
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
"github.com/aws/aws-sdk-go-v2/aws"
11+
"github.com/aws/aws-sdk-go-v2/service/securityhub"
12+
awstypes "github.com/aws/aws-sdk-go-v2/service/securityhub/types"
13+
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
14+
"github.com/hashicorp/terraform-plugin-framework/resource"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
18+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
19+
"github.com/hashicorp/terraform-plugin-framework/types"
20+
"github.com/hashicorp/terraform-provider-aws/internal/create"
21+
"github.com/hashicorp/terraform-provider-aws/internal/errs"
22+
"github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag"
23+
"github.com/hashicorp/terraform-provider-aws/internal/framework"
24+
fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
25+
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
26+
fwvalidators "github.com/hashicorp/terraform-provider-aws/internal/framework/validators"
27+
"github.com/hashicorp/terraform-provider-aws/internal/retry"
28+
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
29+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
30+
"github.com/hashicorp/terraform-provider-aws/names"
31+
)
32+
33+
// @FrameworkResource("aws_securityhub_aggregator_v2", name="Aggregator V2")
34+
// @ArnIdentity
35+
// @Tags(identifierAttribute="arn")
36+
// @Testing(existsType="github.com/aws/aws-sdk-go-v2/service/securityhub;securityhub;securityhub.GetAggregatorV2Output")
37+
// @Testing(serialize=true)
38+
// @Testing(tagsTest=false)
39+
// @Testing(hasNoPreExistingResource=true)
40+
// @Testing(generator=false)
41+
func newAggregatorV2Resource(_ context.Context) (resource.ResourceWithConfigure, error) {
42+
return &aggregatorV2Resource{}, nil
43+
}
44+
45+
type aggregatorV2Resource struct {
46+
framework.ResourceWithModel[aggregatorV2ResourceModel]
47+
framework.WithImportByIdentity
48+
}
49+
50+
func (r *aggregatorV2Resource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) {
51+
response.Schema = schema.Schema{
52+
Attributes: map[string]schema.Attribute{
53+
names.AttrARN: framework.ARNAttributeComputedOnly(),
54+
"aggregation_region": schema.StringAttribute{
55+
Computed: true,
56+
Description: "The AWS Region where data is aggregated.",
57+
PlanModifiers: []planmodifier.String{
58+
stringplanmodifier.UseStateForUnknown(),
59+
},
60+
},
61+
"linked_regions": schema.SetAttribute{
62+
ElementType: types.StringType,
63+
CustomType: fwtypes.SetOfStringType,
64+
Optional: true,
65+
Validators: []validator.Set{
66+
setvalidator.ValueStringsAre(fwvalidators.AWSRegion()),
67+
},
68+
Description: "The list of Regions linked to the aggregation Region.",
69+
},
70+
"region_linking_mode": schema.StringAttribute{
71+
Required: true,
72+
Description: "Determines how Regions are linked: ALL_REGIONS, ALL_REGIONS_EXCEPT_SPECIFIED, or SPECIFIED_REGIONS.",
73+
},
74+
names.AttrTags: tftags.TagsAttribute(),
75+
names.AttrTagsAll: tftags.TagsAttributeComputedOnly(),
76+
},
77+
}
78+
}
79+
80+
func (r *aggregatorV2Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
81+
var data aggregatorV2ResourceModel
82+
response.Diagnostics.Append(request.Plan.Get(ctx, &data)...)
83+
if response.Diagnostics.HasError() {
84+
return
85+
}
86+
87+
conn := r.Meta().SecurityHubClient(ctx)
88+
89+
var input securityhub.CreateAggregatorV2Input
90+
response.Diagnostics.Append(fwflex.Expand(ctx, data, &input)...)
91+
if response.Diagnostics.HasError() {
92+
return
93+
}
94+
95+
// Additional fields.
96+
input.ClientToken = aws.String(create.UniqueId(ctx))
97+
input.Tags = getTagsIn(ctx)
98+
99+
output, err := conn.CreateAggregatorV2(ctx, &input)
100+
101+
if err != nil {
102+
response.Diagnostics.AddError("creating Security Hub V2 Aggregator", err.Error())
103+
return
104+
}
105+
106+
// Set values for unknowns.
107+
data.ARN = fwflex.StringToFramework(ctx, output.AggregatorV2Arn)
108+
data.AggregationRegion = fwflex.StringToFramework(ctx, output.AggregationRegion)
109+
110+
response.Diagnostics.Append(response.State.Set(ctx, data)...)
111+
}
112+
113+
func (r *aggregatorV2Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
114+
var data aggregatorV2ResourceModel
115+
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
116+
if response.Diagnostics.HasError() {
117+
return
118+
}
119+
120+
conn := r.Meta().SecurityHubClient(ctx)
121+
122+
arn := fwflex.StringValueFromFramework(ctx, data.ARN)
123+
output, err := findAggregatorV2ByARN(ctx, conn, arn)
124+
125+
if retry.NotFound(err) {
126+
response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err))
127+
response.State.RemoveResource(ctx)
128+
return
129+
}
130+
131+
if err != nil {
132+
response.Diagnostics.AddError(fmt.Sprintf("reading Security Hub V2 Aggregator (%s)", arn), err.Error())
133+
return
134+
}
135+
136+
// Set attributes for import.
137+
response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...)
138+
if response.Diagnostics.HasError() {
139+
return
140+
}
141+
142+
response.Diagnostics.Append(response.State.Set(ctx, &data)...)
143+
}
144+
145+
func (r *aggregatorV2Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
146+
var old, new aggregatorV2ResourceModel
147+
response.Diagnostics.Append(request.Plan.Get(ctx, &new)...)
148+
if response.Diagnostics.HasError() {
149+
return
150+
}
151+
response.Diagnostics.Append(request.State.Get(ctx, &old)...)
152+
if response.Diagnostics.HasError() {
153+
return
154+
}
155+
156+
conn := r.Meta().SecurityHubClient(ctx)
157+
158+
if !new.RegionLinkingMode.Equal(old.RegionLinkingMode) || !new.LinkedRegions.Equal(old.LinkedRegions) {
159+
arn := fwflex.StringValueFromFramework(ctx, new.ARN)
160+
var input securityhub.UpdateAggregatorV2Input
161+
response.Diagnostics.Append(fwflex.Expand(ctx, new, &input)...)
162+
if response.Diagnostics.HasError() {
163+
return
164+
}
165+
166+
// Additional fields.
167+
input.AggregatorV2Arn = aws.String(arn)
168+
169+
_, err := conn.UpdateAggregatorV2(ctx, &input)
170+
171+
if err != nil {
172+
response.Diagnostics.AddError(fmt.Sprintf("updating Security Hub V2 Aggregator (%s)", arn), err.Error())
173+
return
174+
}
175+
}
176+
177+
response.Diagnostics.Append(response.State.Set(ctx, &new)...)
178+
}
179+
180+
func (r *aggregatorV2Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
181+
var data aggregatorV2ResourceModel
182+
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
183+
if response.Diagnostics.HasError() {
184+
return
185+
}
186+
187+
conn := r.Meta().SecurityHubClient(ctx)
188+
189+
arn := fwflex.StringValueFromFramework(ctx, data.ARN)
190+
input := securityhub.DeleteAggregatorV2Input{
191+
AggregatorV2Arn: aws.String(arn),
192+
}
193+
_, err := conn.DeleteAggregatorV2(ctx, &input)
194+
195+
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
196+
return
197+
}
198+
199+
if err != nil {
200+
response.Diagnostics.AddError(fmt.Sprintf("deleting Security Hub V2 Aggregator (%s)", arn), err.Error())
201+
return
202+
}
203+
}
204+
205+
func findAggregatorV2ByARN(ctx context.Context, conn *securityhub.Client, arn string) (*securityhub.GetAggregatorV2Output, error) {
206+
input := securityhub.GetAggregatorV2Input{
207+
AggregatorV2Arn: aws.String(arn),
208+
}
209+
210+
return findAggregatorV2(ctx, conn, &input)
211+
}
212+
213+
func findAggregatorV2(ctx context.Context, conn *securityhub.Client, input *securityhub.GetAggregatorV2Input) (*securityhub.GetAggregatorV2Output, error) {
214+
output, err := conn.GetAggregatorV2(ctx, input)
215+
216+
if errs.IsA[*awstypes.ResourceNotFoundException](err) || errs.IsAErrorMessageContains[*awstypes.ConflictException](err, "Security Hub V2 is not enabled") {
217+
return nil, &retry.NotFoundError{
218+
LastError: err,
219+
}
220+
}
221+
222+
if err != nil {
223+
return nil, err
224+
}
225+
226+
if output == nil {
227+
return nil, tfresource.NewEmptyResultError()
228+
}
229+
230+
return output, nil
231+
}
232+
233+
type aggregatorV2ResourceModel struct {
234+
framework.WithRegionModel
235+
ARN types.String `tfsdk:"arn"`
236+
AggregationRegion types.String `tfsdk:"aggregation_region"`
237+
LinkedRegions fwtypes.SetOfString `tfsdk:"linked_regions"`
238+
RegionLinkingMode types.String `tfsdk:"region_linking_mode"`
239+
Tags tftags.Map `tfsdk:"tags"`
240+
TagsAll tftags.Map `tfsdk:"tags_all"`
241+
}

0 commit comments

Comments
 (0)