diff --git a/.changelog/34850.txt b/.changelog/34850.txt new file mode 100644 index 00000000000..fdb6a6ee15f --- /dev/null +++ b/.changelog/34850.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_codedeploy_deployment_config: Add `zonal_config` argument +``` \ No newline at end of file diff --git a/internal/service/deploy/deployment_config.go b/internal/service/deploy/deployment_config.go index da392ee8920..14000d17464 100644 --- a/internal/service/deploy/deployment_config.go +++ b/internal/service/deploy/deployment_config.go @@ -134,6 +134,47 @@ func resourceDeploymentConfig() *schema.Resource { }, }, }, + "zonal_config": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "first_zone_monitor_duration_in_seconds": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "minimum_healthy_hosts_per_zone": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrType: { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateDiagFunc: enum.Validate[types.MinimumHealthyHostsPerZoneType](), + }, + names.AttrValue: { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + "monitor_duration_in_seconds": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + }, }, } } @@ -148,6 +189,7 @@ func resourceDeploymentConfigCreate(ctx context.Context, d *schema.ResourceData, DeploymentConfigName: aws.String(name), MinimumHealthyHosts: expandMinimumHealthyHosts(d), TrafficRoutingConfig: expandTrafficRoutingConfig(d), + ZonalConfig: expandZonalConfig(d), } _, err := conn.CreateDeploymentConfig(ctx, input) @@ -195,6 +237,9 @@ func resourceDeploymentConfigRead(ctx context.Context, d *schema.ResourceData, m if err := d.Set("traffic_routing_config", flattenTrafficRoutingConfig(deploymentConfig.TrafficRoutingConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting traffic_routing_config: %s", err) } + if err := d.Set("zonal_config", flattenZonalConfig(deploymentConfig.ZonalConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting zonal_config: %s", err) + } return diags } @@ -241,116 +286,186 @@ func findDeploymentConfigByName(ctx context.Context, conn *codedeploy.Client, na } func expandMinimumHealthyHosts(d *schema.ResourceData) *types.MinimumHealthyHosts { - hosts, ok := d.GetOk("minimum_healthy_hosts") + v, ok := d.GetOk("minimum_healthy_hosts") if !ok { return nil } - host := hosts.([]interface{})[0].(map[string]interface{}) - minimumHealthyHost := types.MinimumHealthyHosts{ - Type: types.MinimumHealthyHostsType(host[names.AttrType].(string)), - Value: int32(host[names.AttrValue].(int)), + tfMap := v.([]interface{})[0].(map[string]interface{}) + + apiObject := &types.MinimumHealthyHosts{ + Type: types.MinimumHealthyHostsType(tfMap[names.AttrType].(string)), + Value: int32(tfMap[names.AttrValue].(int)), } - return &minimumHealthyHost + return apiObject } func expandTrafficRoutingConfig(d *schema.ResourceData) *types.TrafficRoutingConfig { - block, ok := d.GetOk("traffic_routing_config") + v, ok := d.GetOk("traffic_routing_config") if !ok { return nil } - config := block.([]interface{})[0].(map[string]interface{}) - trafficRoutingConfig := types.TrafficRoutingConfig{} - if trafficType, ok := config[names.AttrType]; ok { - trafficRoutingConfig.Type = types.TrafficRoutingType(trafficType.(string)) + tfMap := v.([]interface{})[0].(map[string]interface{}) + apiObject := &types.TrafficRoutingConfig{} + + if v, ok := tfMap["time_based_canary"]; ok && len(v.([]interface{})) > 0 { + apiObject.TimeBasedCanary = expandTimeBasedCanary(v.([]interface{})[0].(map[string]interface{})) + } + if v, ok := tfMap["time_based_linear"]; ok && len(v.([]interface{})) > 0 { + apiObject.TimeBasedLinear = expandTimeBasedLinear(v.([]interface{})[0].(map[string]interface{})) + } + if v, ok := tfMap[names.AttrType]; ok { + apiObject.Type = types.TrafficRoutingType(v.(string)) + } + + return apiObject +} + +func expandTimeBasedCanary(tfMap map[string]interface{}) *types.TimeBasedCanary { + apiObject := &types.TimeBasedCanary{} + + if v, ok := tfMap[names.AttrInterval]; ok { + apiObject.CanaryInterval = int32(v.(int)) + } + if v, ok := tfMap["percentage"]; ok { + apiObject.CanaryPercentage = int32(v.(int)) } - if canary, ok := config["time_based_canary"]; ok && len(canary.([]interface{})) > 0 { - canaryConfig := canary.([]interface{})[0].(map[string]interface{}) - trafficRoutingConfig.TimeBasedCanary = expandTimeBasedCanary(canaryConfig) + + return apiObject +} + +func expandTimeBasedLinear(tfMap map[string]interface{}) *types.TimeBasedLinear { + apiObject := &types.TimeBasedLinear{} + + if v, ok := tfMap[names.AttrInterval]; ok { + apiObject.LinearInterval = int32(v.(int)) } - if linear, ok := config["time_based_linear"]; ok && len(linear.([]interface{})) > 0 { - linearConfig := linear.([]interface{})[0].(map[string]interface{}) - trafficRoutingConfig.TimeBasedLinear = expandTimeBasedLinear(linearConfig) + if v, ok := tfMap["percentage"]; ok { + apiObject.LinearPercentage = int32(v.(int)) } - return &trafficRoutingConfig + return apiObject } -func expandTimeBasedCanary(config map[string]interface{}) *types.TimeBasedCanary { - canary := types.TimeBasedCanary{} - if interval, ok := config[names.AttrInterval]; ok { - canary.CanaryInterval = int32(interval.(int)) +func expandZonalConfig(d *schema.ResourceData) *types.ZonalConfig { + v, ok := d.GetOk("zonal_config") + if !ok { + return nil + } + + tfMap := v.([]interface{})[0].(map[string]interface{}) + apiObject := &types.ZonalConfig{} + + if v, ok := tfMap["first_zone_monitor_duration_in_seconds"].(int); ok { + apiObject.FirstZoneMonitorDurationInSeconds = aws.Int64(int64(v)) } - if percentage, ok := config["percentage"]; ok { - canary.CanaryPercentage = int32(percentage.(int)) + if v, ok := tfMap["minimum_healthy_hosts_per_zone"]; ok && len(v.([]interface{})) > 0 { + apiObject.MinimumHealthyHostsPerZone = expandMinimumHealthyHostsPerZone(v.([]interface{})[0].(map[string]interface{})) } - return &canary + if v, ok := tfMap["monitor_duration_in_seconds"].(int); ok { + apiObject.MonitorDurationInSeconds = aws.Int64(int64(v)) + } + + return apiObject } -func expandTimeBasedLinear(config map[string]interface{}) *types.TimeBasedLinear { - linear := types.TimeBasedLinear{} - if interval, ok := config[names.AttrInterval]; ok { - linear.LinearInterval = int32(interval.(int)) +func expandMinimumHealthyHostsPerZone(tfMap map[string]interface{}) *types.MinimumHealthyHostsPerZone { + if tfMap == nil { + return nil } - if percentage, ok := config["percentage"]; ok { - linear.LinearPercentage = int32(percentage.(int)) + + apiObject := &types.MinimumHealthyHostsPerZone{ + Type: types.MinimumHealthyHostsPerZoneType(tfMap[names.AttrType].(string)), + Value: int32(tfMap[names.AttrValue].(int)), } - return &linear + + return apiObject } -func flattenMinimumHealthHosts(hosts *types.MinimumHealthyHosts) []map[string]interface{} { - result := make([]map[string]interface{}, 0) - if hosts == nil { - return result +func flattenMinimumHealthHosts(apiObject *types.MinimumHealthyHosts) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return tfList } - item := make(map[string]interface{}) + tfMap := make(map[string]interface{}) + tfMap[names.AttrType] = apiObject.Type + tfMap[names.AttrValue] = apiObject.Value - item[names.AttrType] = string(hosts.Type) - item[names.AttrValue] = hosts.Value + return append(tfList, tfMap) +} - return append(result, item) +func flattenTrafficRoutingConfig(apiObject *types.TrafficRoutingConfig) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return tfList + } + + tfMap := make(map[string]interface{}) + tfMap["time_based_canary"] = flattenTimeBasedCanary(apiObject.TimeBasedCanary) + tfMap["time_based_linear"] = flattenTimeBasedLinear(apiObject.TimeBasedLinear) + tfMap[names.AttrType] = apiObject.Type + + return append(tfList, tfMap) } -func flattenTrafficRoutingConfig(config *types.TrafficRoutingConfig) []map[string]interface{} { - result := make([]map[string]interface{}, 0) - if config == nil { - return result +func flattenTimeBasedCanary(apiObject *types.TimeBasedCanary) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return tfList } - item := make(map[string]interface{}) + tfMap := make(map[string]interface{}) + tfMap[names.AttrInterval] = apiObject.CanaryInterval + tfMap["percentage"] = apiObject.CanaryPercentage - item[names.AttrType] = string(config.Type) - item["time_based_canary"] = flattenTimeBasedCanary(config.TimeBasedCanary) - item["time_based_linear"] = flattenTimeBasedLinear(config.TimeBasedLinear) + return append(tfList, tfMap) +} - return append(result, item) +func flattenTimeBasedLinear(apiObject *types.TimeBasedLinear) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return tfList + } + + tfMap := make(map[string]interface{}) + tfMap[names.AttrInterval] = apiObject.LinearInterval + tfMap["percentage"] = apiObject.LinearPercentage + + return append(tfList, tfMap) } -func flattenTimeBasedCanary(canary *types.TimeBasedCanary) []map[string]interface{} { - result := make([]map[string]interface{}, 0) - if canary == nil { - return result +func flattenZonalConfig(apiObject *types.ZonalConfig) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return nil } - item := make(map[string]interface{}) - item[names.AttrInterval] = canary.CanaryInterval - item["percentage"] = canary.CanaryPercentage + tfMap := make(map[string]interface{}) + tfMap["first_zone_monitor_duration_in_seconds"] = aws.ToInt64(apiObject.FirstZoneMonitorDurationInSeconds) + tfMap["minimum_healthy_hosts_per_zone"] = flattenMinimumHealthHostsPerZone(apiObject.MinimumHealthyHostsPerZone) + tfMap["monitor_duration_in_seconds"] = aws.ToInt64(apiObject.MonitorDurationInSeconds) - return append(result, item) + return append(tfList, tfMap) } -func flattenTimeBasedLinear(linear *types.TimeBasedLinear) []map[string]interface{} { - result := make([]map[string]interface{}, 0) - if linear == nil { - return result +func flattenMinimumHealthHostsPerZone(apiObject *types.MinimumHealthyHostsPerZone) []interface{} { + tfList := make([]interface{}, 0) + + if apiObject == nil { + return nil } - item := make(map[string]interface{}) - item[names.AttrInterval] = linear.LinearInterval - item["percentage"] = linear.LinearPercentage + tfMap := make(map[string]interface{}) + tfMap[names.AttrType] = apiObject.Type + tfMap[names.AttrValue] = apiObject.Value - return append(result, item) + return append(tfList, tfMap) } diff --git a/internal/service/deploy/deployment_config_test.go b/internal/service/deploy/deployment_config_test.go index 6b996db4b2c..2d013b300f3 100644 --- a/internal/service/deploy/deployment_config_test.go +++ b/internal/service/deploy/deployment_config_test.go @@ -35,12 +35,13 @@ func TestAccDeployDeploymentConfig_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccDeploymentConfigConfig_fleet(rName, 75), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDeploymentConfigExists(ctx, resourceName, &config), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, "deployment_config_name", rName), resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "zonal_config.#", acctest.Ct0), ), }, { @@ -52,6 +53,30 @@ func TestAccDeployDeploymentConfig_basic(t *testing.T) { }) } +func TestAccDeployDeploymentConfig_disappears(t *testing.T) { + ctx := acctest.Context(t) + var config types.DeploymentConfigInfo + resourceName := "aws_codedeploy_deployment_config.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DeployServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDeploymentConfigDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDeploymentConfigConfig_fleet(rName, 75), + Check: resource.ComposeTestCheckFunc( + testAccCheckDeploymentConfigExists(ctx, resourceName, &config), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfcodedeploy.ResourceDeploymentConfig(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccDeployDeploymentConfig_fleetPercent(t *testing.T) { ctx := acctest.Context(t) var config1, config2 types.DeploymentConfigInfo @@ -240,6 +265,53 @@ func TestAccDeployDeploymentConfig_trafficLinear(t *testing.T) { }) } +func TestAccDeployDeploymentConfig_zonalConfig(t *testing.T) { + ctx := acctest.Context(t) + var config1, config2 types.DeploymentConfigInfo + resourceName := "aws_codedeploy_deployment_config.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DeployServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDeploymentConfigDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDeploymentConfigConfig_zonalConfig(rName, 10, "FLEET_PERCENT", 20, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckDeploymentConfigExists(ctx, resourceName, &config1), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.first_zone_monitor_duration_in_seconds", acctest.Ct10), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.0.type", "FLEET_PERCENT"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.0.value", "20"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.monitor_duration_in_seconds", acctest.Ct10), + ), + }, + { + Config: testAccDeploymentConfigConfig_zonalConfig(rName, 20, "HOST_COUNT", 2, 20), + Check: resource.ComposeTestCheckFunc( + testAccCheckDeploymentConfigExists(ctx, resourceName, &config2), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.first_zone_monitor_duration_in_seconds", "20"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.0.type", "HOST_COUNT"), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.minimum_healthy_hosts_per_zone.0.value", acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, "zonal_config.0.monitor_duration_in_seconds", "20"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckDeploymentConfigDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).DeployClient(ctx) @@ -358,3 +430,26 @@ resource "aws_codedeploy_deployment_config" "test" { } `, rName, interval, percentage) } + +func testAccDeploymentConfigConfig_zonalConfig(rName string, first_zone_monitor_duration int, minimum_healthy_host_type string, minimum_healthy_host_value int, monitor_duration int) string { + return fmt.Sprintf(` +resource "aws_codedeploy_deployment_config" "test" { + deployment_config_name = %[1]q + compute_platform = "Server" + + minimum_healthy_hosts { + type = "HOST_COUNT" + value = 3 + } + + zonal_config { + first_zone_monitor_duration_in_seconds = %[2]d + minimum_healthy_hosts_per_zone { + type = %[3]q + value = %[4]d + } + monitor_duration_in_seconds = %[5]d + } +} +`, rName, first_zone_monitor_duration, minimum_healthy_host_type, minimum_healthy_host_value, monitor_duration) +} diff --git a/internal/service/deploy/deployment_group_test.go b/internal/service/deploy/deployment_group_test.go index 5f30962d164..1cf7b511720 100644 --- a/internal/service/deploy/deployment_group_test.go +++ b/internal/service/deploy/deployment_group_test.go @@ -1072,6 +1072,45 @@ func TestAccDeployDeploymentGroup_LoadBalancerInfoTargetGroupInfo_delete(t *test }) } +func TestAccDeployDeploymentGroup_LoadBalancerInfoTargetGroupInfo_multiple(t *testing.T) { + ctx := acctest.Context(t) + var group types.DeploymentGroupInfo + resourceName := "aws_codedeploy_deployment_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.DeployServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDeploymentGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDeploymentGroupConfig_loadBalancerInfoTargetInfoMultiple(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckDeploymentGroupExists(ctx, resourceName, &group), + resource.TestCheckResourceAttr(resourceName, "load_balancer_info.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "load_balancer_info.0.target_group_info.#", acctest.Ct3), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "load_balancer_info.0.target_group_info.*", map[string]string{ + names.AttrName: "acc-test-codedeploy-dep-group-1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "load_balancer_info.0.target_group_info.*", map[string]string{ + names.AttrName: "acc-test-codedeploy-dep-group-2", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "load_balancer_info.0.target_group_info.*", map[string]string{ + names.AttrName: "acc-test-codedeploy-dep-group-3", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccDeploymentGroupImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, + }, + }) +} + func TestAccDeployDeploymentGroup_InPlaceDeploymentWithTrafficControl_create(t *testing.T) { ctx := acctest.Context(t) var group types.DeploymentGroupInfo @@ -2182,6 +2221,30 @@ resource "aws_codedeploy_deployment_group" "test" { `, rName)) } +func testAccDeploymentGroupConfig_loadBalancerInfoTargetInfoMultiple(rName string) string { + return acctest.ConfigCompose(testAccDeploymentGroupConfig_base(rName), fmt.Sprintf(` +resource "aws_codedeploy_deployment_group" "test" { + app_name = aws_codedeploy_app.test.name + deployment_group_name = %[1]q + service_role_arn = aws_iam_role.test.arn + + load_balancer_info { + target_group_info { + name = "acc-test-codedeploy-dep-group-1" + } + + target_group_info { + name = "acc-test-codedeploy-dep-group-2" + } + + target_group_info { + name = "acc-test-codedeploy-dep-group-3" + } + } +} +`, rName)) +} + func testAccDeploymentGroupConfig_inPlaceTrafficControlCreate(rName string) string { return acctest.ConfigCompose(testAccDeploymentGroupConfig_base(rName), fmt.Sprintf(` resource "aws_codedeploy_deployment_group" "test" { diff --git a/website/docs/r/codedeploy_deployment_config.html.markdown b/website/docs/r/codedeploy_deployment_config.html.markdown index 8e7f1d4fbab..2c0a1674392 100644 --- a/website/docs/r/codedeploy_deployment_config.html.markdown +++ b/website/docs/r/codedeploy_deployment_config.html.markdown @@ -97,6 +97,7 @@ This resource supports the following arguments: * `compute_platform` - (Optional) The compute platform can be `Server`, `Lambda`, or `ECS`. Default is `Server`. * `minimum_healthy_hosts` - (Optional) A minimum_healthy_hosts block. Required for `Server` compute platform. Minimum Healthy Hosts are documented below. * `traffic_routing_config` - (Optional) A traffic_routing_config block. Traffic Routing Config is documented below. +* `zonal_config` - (Optional) A zonal_config block. Zonal Config is documented below. The `minimum_healthy_hosts` block supports the following: @@ -122,6 +123,17 @@ The `time_based_linear` block supports the following: * `interval` - (Optional) The number of minutes between each incremental traffic shift of a `TimeBasedLinear` deployment. * `percentage` - (Optional) The percentage of traffic that is shifted at the start of each increment of a `TimeBasedLinear` deployment. +The `zonal_config` block supports the following: + +* `first_zone_monitor_duration_in_seconds` - (Optional) The period of time, in seconds, that CodeDeploy must wait after completing a deployment to the first Availability Zone. CodeDeploy will wait this amount of time before starting a deployment to the second Availability Zone. If you don't specify a value for `first_zone_monitor_duration_in_seconds`, then CodeDeploy uses the `monitor_duration_in_seconds` value for the first Availability Zone. +* `minimum_healthy_hosts_per_zone` - (Optional) The number or percentage of instances that must remain available per Availability Zone during a deployment. If you don't specify a value under `minimum_healthy_hosts_per_zone`, then CodeDeploy uses a default value of 0 percent. This block is more documented below. +* `monitor_duration_in_seconds` - (Optional) The period of time, in seconds, that CodeDeploy must wait after completing a deployment to an Availability Zone. CodeDeploy will wait this amount of time before starting a deployment to the next Availability Zone. If you don't specify a `monitor_duration_in_seconds`, CodeDeploy starts deploying to the next Availability Zone immediately. + +The `minimum_healthy_hosts_per_zone` block supports the following: + +* `type` - (Required) The type can either be `FLEET_PERCENT` or `HOST_COUNT`. +* `value` - (Required) The value when the type is `FLEET_PERCENT` represents the minimum number of healthy instances as a percentage of the total number of instances in the Availability Zone during a deployment. If you specify FLEET_PERCENT, at the start of the deployment, AWS CodeDeploy converts the percentage to the equivalent number of instance and rounds up fractional instances. When the type is `HOST_COUNT`, the value represents the minimum number of healthy instances in the Availability Zone as an absolute value. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: