@@ -252,6 +252,20 @@ func resourceLoadBalancer() *schema.Resource {
252
252
Default : awstypes .LoadBalancerTypeEnumApplication ,
253
253
ValidateDiagFunc : enum .Validate [awstypes.LoadBalancerTypeEnum ](),
254
254
},
255
+ "minimum_load_balancer_capacity" : {
256
+ Type : schema .TypeList ,
257
+ Optional : true ,
258
+ MaxItems : 1 ,
259
+ DiffSuppressFunc : suppressIfLBTypeNot (awstypes .LoadBalancerTypeEnumApplication , awstypes .LoadBalancerTypeEnumNetwork ),
260
+ Elem : & schema.Resource {
261
+ Schema : map [string ]* schema.Schema {
262
+ "capacity_units" : {
263
+ Type : schema .TypeInt ,
264
+ Required : true ,
265
+ },
266
+ },
267
+ },
268
+ },
255
269
names .AttrName : {
256
270
Type : schema .TypeString ,
257
271
Optional : true ,
@@ -445,6 +459,7 @@ func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, met
445
459
}
446
460
447
461
var attributes []awstypes.LoadBalancerAttribute
462
+ var minCapacity * awstypes.MinimumLoadBalancerCapacity
448
463
449
464
if lbType == awstypes .LoadBalancerTypeEnumApplication || lbType == awstypes .LoadBalancerTypeEnumNetwork {
450
465
if v , ok := d .GetOk ("access_logs" ); ok && len (v .([]any )) > 0 && v .([]any )[0 ] != nil {
@@ -455,6 +470,9 @@ func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, met
455
470
Value : flex .BoolValueToString (false ),
456
471
})
457
472
}
473
+ if v , ok := d .GetOk ("minimum_load_balancer_capacity" ); ok && len (v .([]any )) > 0 && v .([]any )[0 ] != nil {
474
+ minCapacity = expandMinimumLoadBalancerCapacity (v .([]any ))
475
+ }
458
476
}
459
477
460
478
if lbType == awstypes .LoadBalancerTypeEnumApplication {
@@ -470,6 +488,16 @@ func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, met
470
488
471
489
attributes = append (attributes , loadBalancerAttributes .expand (d , lbType , false )... )
472
490
491
+ if minCapacity != nil {
492
+ if err := modifyCapacityReservation (ctx , conn , d .Id (), minCapacity ); err != nil {
493
+ return sdkdiag .AppendFromErr (diags , err )
494
+ }
495
+
496
+ if _ , err := waitCapacityReservationProvisioned (ctx , conn , d .Id (), d .Timeout (schema .TimeoutCreate )); err != nil {
497
+ return sdkdiag .AppendErrorf (diags , "waiting for ELBv2 Load Balancer (%s) capacity reservation provision: %s" , d .Id (), err )
498
+ }
499
+ }
500
+
473
501
wait := false
474
502
if len (attributes ) > 0 {
475
503
if err := modifyLoadBalancerAttributes (ctx , conn , d .Id (), attributes ); err != nil {
@@ -564,6 +592,18 @@ func resourceLoadBalancerRead(ctx context.Context, d *schema.ResourceData, meta
564
592
565
593
loadBalancerAttributes .flatten (d , attributes )
566
594
595
+ if lb .Type == awstypes .LoadBalancerTypeEnumApplication || lb .Type == awstypes .LoadBalancerTypeEnumNetwork {
596
+ capacity , err := findCapacityReservationByARN (ctx , conn , d .Id ())
597
+
598
+ if err != nil {
599
+ return sdkdiag .AppendErrorf (diags , "reading ELBv2 Load Balancer (%s) capacity reservation: %s" , d .Id (), err )
600
+ }
601
+
602
+ if err := d .Set ("minimum_load_balancer_capacity" , flattenMinimumLoadBalancerCapacity (capacity .MinimumLoadBalancerCapacity )); err != nil {
603
+ return sdkdiag .AppendErrorf (diags , "setting minimum_load_balancer_capacity: %s" , err )
604
+ }
605
+ }
606
+
567
607
return diags
568
608
}
569
609
@@ -677,6 +717,16 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met
677
717
}
678
718
}
679
719
720
+ if d .HasChange ("minimum_load_balancer_capacity" ) {
721
+ if err := modifyCapacityReservation (ctx , conn , d .Id (), expandMinimumLoadBalancerCapacity (d .Get ("minimum_load_balancer_capacity" ).([]any ))); err != nil {
722
+ return sdkdiag .AppendFromErr (diags , err )
723
+ }
724
+
725
+ if _ , err := waitCapacityReservationProvisioned (ctx , conn , d .Id (), d .Timeout (schema .TimeoutUpdate )); err != nil {
726
+ return sdkdiag .AppendErrorf (diags , "waiting for ELBv2 Load Balancer (%s) capacity reservation provision: %s" , d .Id (), err )
727
+ }
728
+ }
729
+
680
730
if _ , err := waitLoadBalancerActive (ctx , conn , d .Id (), d .Timeout (schema .TimeoutUpdate )); err != nil {
681
731
return sdkdiag .AppendErrorf (diags , "waiting for ELBv2 Load Balancer (%s) update: %s" , d .Id (), err )
682
732
}
@@ -717,7 +767,7 @@ func resourceLoadBalancerDelete(ctx context.Context, d *schema.ResourceData, met
717
767
}
718
768
719
769
func modifyLoadBalancerAttributes (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string , attributes []awstypes.LoadBalancerAttribute ) error {
720
- input := & elasticloadbalancingv2.ModifyLoadBalancerAttributesInput {
770
+ input := elasticloadbalancingv2.ModifyLoadBalancerAttributesInput {
721
771
Attributes : attributes ,
722
772
LoadBalancerArn : aws .String (arn ),
723
773
}
@@ -728,7 +778,7 @@ func modifyLoadBalancerAttributes(ctx context.Context, conn *elasticloadbalancin
728
778
return nil
729
779
}
730
780
731
- _ , err := conn .ModifyLoadBalancerAttributes (ctx , input )
781
+ _ , err := conn .ModifyLoadBalancerAttributes (ctx , & input )
732
782
733
783
if err != nil {
734
784
// "Validation error: Load balancer attribute key 'routing.http.desync_mitigation_mode' is not recognized"
@@ -750,6 +800,28 @@ func modifyLoadBalancerAttributes(ctx context.Context, conn *elasticloadbalancin
750
800
}
751
801
}
752
802
803
+ func modifyCapacityReservation (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string , minCapacity * awstypes.MinimumLoadBalancerCapacity ) error {
804
+ resetCapacityReservation := false
805
+ if minCapacity == nil {
806
+ resetCapacityReservation = true
807
+ } else if minCapacity .CapacityUnits == nil {
808
+ resetCapacityReservation = true
809
+ }
810
+ input := elasticloadbalancingv2.ModifyCapacityReservationInput {
811
+ LoadBalancerArn : aws .String (arn ),
812
+ MinimumLoadBalancerCapacity : minCapacity ,
813
+ ResetCapacityReservation : aws .Bool (resetCapacityReservation ),
814
+ }
815
+
816
+ _ , err := conn .ModifyCapacityReservation (ctx , & input )
817
+
818
+ if err != nil {
819
+ return fmt .Errorf ("modifying ELBv2 Load Balancer (%s) capacity reservation: %w" , arn , err )
820
+ }
821
+
822
+ return nil
823
+ }
824
+
753
825
type loadBalancerAttributeInfo struct {
754
826
apiAttributeKey string
755
827
tfType schema.ValueType
@@ -950,11 +1022,11 @@ func findLoadBalancerByARN(ctx context.Context, conn *elasticloadbalancingv2.Cli
950
1022
}
951
1023
952
1024
func findLoadBalancerAttributesByARN (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string ) ([]awstypes.LoadBalancerAttribute , error ) {
953
- input := & elasticloadbalancingv2.DescribeLoadBalancerAttributesInput {
1025
+ input := elasticloadbalancingv2.DescribeLoadBalancerAttributesInput {
954
1026
LoadBalancerArn : aws .String (arn ),
955
1027
}
956
1028
957
- output , err := conn .DescribeLoadBalancerAttributes (ctx , input )
1029
+ output , err := conn .DescribeLoadBalancerAttributes (ctx , & input )
958
1030
959
1031
if errs.IsA [* awstypes.LoadBalancerNotFoundException ](err ) {
960
1032
return nil , & retry.NotFoundError {
@@ -974,6 +1046,31 @@ func findLoadBalancerAttributesByARN(ctx context.Context, conn *elasticloadbalan
974
1046
return output .Attributes , nil
975
1047
}
976
1048
1049
+ func findCapacityReservationByARN (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string ) (* elasticloadbalancingv2.DescribeCapacityReservationOutput , error ) {
1050
+ input := elasticloadbalancingv2.DescribeCapacityReservationInput {
1051
+ LoadBalancerArn : aws .String (arn ),
1052
+ }
1053
+
1054
+ output , err := conn .DescribeCapacityReservation (ctx , & input )
1055
+
1056
+ if errs.IsA [* awstypes.LoadBalancerNotFoundException ](err ) {
1057
+ return nil , & retry.NotFoundError {
1058
+ LastError : err ,
1059
+ LastRequest : input ,
1060
+ }
1061
+ }
1062
+
1063
+ if err != nil {
1064
+ return nil , err
1065
+ }
1066
+
1067
+ if output == nil {
1068
+ return nil , tfresource .NewEmptyResultError (input )
1069
+ }
1070
+
1071
+ return output , nil
1072
+ }
1073
+
977
1074
func statusLoadBalancer (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string ) retry.StateRefreshFunc {
978
1075
return func () (any , string , error ) {
979
1076
output , err := findLoadBalancerByARN (ctx , conn , arn )
@@ -990,6 +1087,29 @@ func statusLoadBalancer(ctx context.Context, conn *elasticloadbalancingv2.Client
990
1087
}
991
1088
}
992
1089
1090
+ func statusCapacityReservation (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string ) retry.StateRefreshFunc {
1091
+ return func () (any , string , error ) {
1092
+ output , err := findCapacityReservationByARN (ctx , conn , arn )
1093
+
1094
+ if tfresource .NotFound (err ) {
1095
+ return nil , "" , nil
1096
+ }
1097
+
1098
+ if err != nil {
1099
+ return nil , "" , err
1100
+ }
1101
+
1102
+ overallState := awstypes .CapacityReservationStateEnumProvisioned
1103
+ for _ , rs := range output .CapacityReservationState {
1104
+ if rs .State .Code != awstypes .CapacityReservationStateEnumProvisioned {
1105
+ overallState = rs .State .Code
1106
+ }
1107
+ }
1108
+
1109
+ return output , string (overallState ), nil
1110
+ }
1111
+ }
1112
+
993
1113
func waitLoadBalancerActive (ctx context.Context , conn * elasticloadbalancingv2.Client , arn string , timeout time.Duration ) (* awstypes.LoadBalancer , error ) { //nolint:unparam
994
1114
stateConf := & retry.StateChangeConf {
995
1115
Pending : enum .Slice (awstypes .LoadBalancerStateEnumProvisioning , awstypes .LoadBalancerStateEnumFailed ),
@@ -1076,6 +1196,25 @@ func waitForNLBNetworkInterfacesToDetach(ctx context.Context, conn *ec2.Client,
1076
1196
return err
1077
1197
}
1078
1198
1199
+ func waitCapacityReservationProvisioned (ctx context.Context , conn * elasticloadbalancingv2.Client , lbArn string , timeout time.Duration ) (* elasticloadbalancingv2.DescribeCapacityReservationOutput , error ) { //nolint:unparam
1200
+ stateConf := & retry.StateChangeConf {
1201
+ Pending : enum .Slice (awstypes .CapacityReservationStateEnumPending , awstypes .CapacityReservationStateEnumFailed , awstypes .CapacityReservationStateEnumRebalancing ),
1202
+ Target : enum .Slice (awstypes .CapacityReservationStateEnumProvisioned ),
1203
+ Refresh : statusCapacityReservation (ctx , conn , lbArn ),
1204
+ Timeout : timeout ,
1205
+ MinTimeout : 10 * time .Second ,
1206
+ Delay : 30 * time .Second ,
1207
+ }
1208
+
1209
+ outputRaw , err := stateConf .WaitForStateContext (ctx )
1210
+
1211
+ if output , ok := outputRaw .(* elasticloadbalancingv2.DescribeCapacityReservationOutput ); ok {
1212
+ return output , err
1213
+ }
1214
+
1215
+ return nil , err
1216
+ }
1217
+
1079
1218
func loadBalancerNameFromARN (s string ) (string , error ) {
1080
1219
v , err := arn .Parse (s )
1081
1220
if err != nil {
@@ -1464,13 +1603,47 @@ func expandIPAMPools(tfList []any) *awstypes.IpamPools {
1464
1603
return & apiObject
1465
1604
}
1466
1605
1467
- func flattenIPAMPools (ipamPools * awstypes.IpamPools ) []any {
1468
- if ipamPools == nil {
1606
+ func expandMinimumLoadBalancerCapacity (tfList []any ) * awstypes.MinimumLoadBalancerCapacity {
1607
+ if len (tfList ) == 0 {
1608
+ return nil
1609
+ }
1610
+
1611
+ var apiObject awstypes.MinimumLoadBalancerCapacity
1612
+
1613
+ for _ , tfMapRaw := range tfList {
1614
+ tfMap , ok := tfMapRaw .(map [string ]any )
1615
+
1616
+ if ! ok {
1617
+ continue
1618
+ }
1619
+
1620
+ if v , ok := tfMap ["capacity_units" ].(int ); ok && v != 0 {
1621
+ apiObject .CapacityUnits = aws .Int32 (int32 (v ))
1622
+ }
1623
+ }
1624
+
1625
+ return & apiObject
1626
+ }
1627
+
1628
+ func flattenIPAMPools (apiObject * awstypes.IpamPools ) []any {
1629
+ if apiObject == nil {
1630
+ return nil
1631
+ }
1632
+
1633
+ tfMap := map [string ]any {
1634
+ "ipv4_ipam_pool_id" : aws .ToString (apiObject .Ipv4IpamPoolId ),
1635
+ }
1636
+
1637
+ return []any {tfMap }
1638
+ }
1639
+
1640
+ func flattenMinimumLoadBalancerCapacity (apiObject * awstypes.MinimumLoadBalancerCapacity ) []any {
1641
+ if apiObject == nil {
1469
1642
return nil
1470
1643
}
1471
1644
1472
1645
tfMap := map [string ]any {
1473
- "ipv4_ipam_pool_id " : aws .ToString ( ipamPools . Ipv4IpamPoolId ),
1646
+ "capacity_units " : aws .ToInt32 ( apiObject . CapacityUnits ),
1474
1647
}
1475
1648
1476
1649
return []any {tfMap }
0 commit comments