diff --git a/internal/services/containers/kubernetes_cluster_other_resource_test.go b/internal/services/containers/kubernetes_cluster_other_resource_test.go index 56c5e4d00ab6..7e8f6bdcd22d 100644 --- a/internal/services/containers/kubernetes_cluster_other_resource_test.go +++ b/internal/services/containers/kubernetes_cluster_other_resource_test.go @@ -499,13 +499,43 @@ func TestAccKubernetesCluster_upgradeChannel(t *testing.T) { }) } -func TestAccKubernetesCluster_basicMaintenanceConfig(t *testing.T) { +func TestAccKubernetesCluster_basicMaintenanceConfigAutoUpgrade(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") r := KubernetesClusterResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basicMaintenanceConfig(data), + Config: r.basicMaintenanceConfigAutoUpgrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKubernetesCluster_basicMaintenanceConfigDefault(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicMaintenanceConfigDefault(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKubernetesCluster_basicMaintenanceConfigNodeOs(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicMaintenanceConfigNodeOs(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -529,13 +559,43 @@ func TestAccKubernetesCluster_capacityReservationGroup(t *testing.T) { }) } -func TestAccKubernetesCluster_completeMaintenanceConfig(t *testing.T) { +func TestAccKubernetesCluster_completeMaintenanceConfigAutoUpgrade(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") r := KubernetesClusterResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.completeMaintenanceConfig(data), + Config: r.completeMaintenanceConfigAutoUpgrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKubernetesCluster_completeMaintenanceConfigDefault(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.completeMaintenanceConfigDefault(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKubernetesCluster_completeMaintenanceConfigNodeOs(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_kubernetes_cluster", "test") + r := KubernetesClusterResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.completeMaintenanceConfigNodeOs(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -550,21 +610,64 @@ func TestAccKubernetesCluster_updateMaintenanceConfig(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basicMaintenanceConfig(data), + Config: r.basicMaintenanceConfigDefault(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.completeMaintenanceConfig(data), + Config: r.completeMaintenanceConfigDefault(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, data.ImportStep(), { - Config: r.basicMaintenanceConfig(data), + Config: r.basicMaintenanceConfigDefault(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicMaintenanceConfigAutoUpgrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.completeMaintenanceConfigAutoUpgrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicMaintenanceConfigAutoUpgrade(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + + { + Config: r.basicMaintenanceConfigNodeOs(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.completeMaintenanceConfigNodeOs(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicMaintenanceConfigNodeOs(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -1946,7 +2049,43 @@ resource "azurerm_kubernetes_cluster" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, controlPlaneVersion, upgradeChannel) } -func (KubernetesClusterResource) basicMaintenanceConfig(data acceptance.TestData) string { +func (KubernetesClusterResource) basicMaintenanceConfigAutoUpgrade(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-aks-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + dns_prefix = "acctestaks%d" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + } + identity { + type = "SystemAssigned" + } + maintenance_window_auto_upgrade { + frequency = "Weekly" + interval = 1 + day_of_week = "Monday" + start_time = "07:00" + utc_offset = "+01:00" + duration = 8 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (KubernetesClusterResource) basicMaintenanceConfigDefault(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -1980,6 +2119,41 @@ resource "azurerm_kubernetes_cluster" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) } +func (KubernetesClusterResource) basicMaintenanceConfigNodeOs(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-aks-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + dns_prefix = "acctestaks%d" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + } + identity { + type = "SystemAssigned" + } + maintenance_window_node_os { + frequency = "Daily" + interval = 1 + start_time = "07:00" + utc_offset = "+01:00" + duration = 16 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + func (KubernetesClusterResource) capacityReservationGroup(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -2044,7 +2218,55 @@ resource "azurerm_kubernetes_cluster" "test" { `, data.RandomInteger, data.Locations.Primary) } -func (KubernetesClusterResource) completeMaintenanceConfig(data acceptance.TestData) string { +func (KubernetesClusterResource) completeMaintenanceConfigAutoUpgrade(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-aks-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + dns_prefix = "acctestaks%d" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + } + identity { + type = "SystemAssigned" + } + maintenance_window_auto_upgrade { + frequency = "RelativeMonthly" + interval = 2 + duration = 8 + + day_of_week = "Monday" + week_index = "First" + start_time = "07:00" + utc_offset = "+01:00" + start_date = "2023-11-26T00:00:00Z" + + not_allowed { + end = "2023-11-30T00:00:00Z" + start = "2023-11-26T00:00:00Z" + } + not_allowed { + end = "2023-12-30T00:00:00Z" + start = "2023-12-26T00:00:00Z" + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (KubernetesClusterResource) completeMaintenanceConfigDefault(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -2082,9 +2304,64 @@ resource "azurerm_kubernetes_cluster" "test" { hours = [1, 2] } allowed { - day = "Sunday" + day = "Thursday" + hours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] + } + allowed { + day = "Friday" hours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] } + allowed { + day = "Saturday" + hours = [10, 11, 12, 13, 14, 15, 16] + } + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) +} + +func (KubernetesClusterResource) completeMaintenanceConfigNodeOs(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-aks-%d" + location = "%s" +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + dns_prefix = "acctestaks%d" + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + } + identity { + type = "SystemAssigned" + } + maintenance_window_node_os { + frequency = "AbsoluteMonthly" + interval = 1 + duration = 9 + + day_of_month = 5 + start_time = "07:00" + utc_offset = "+01:00" + start_date = "2023-11-26T00:00:00Z" + + not_allowed { + end = "2023-11-30T00:00:00Z" + start = "2023-11-26T00:00:00Z" + } + not_allowed { + end = "2023-12-30T00:00:00Z" + start = "2023-12-26T00:00:00Z" + } } } `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger) diff --git a/internal/services/containers/kubernetes_cluster_resource.go b/internal/services/containers/kubernetes_cluster_resource.go index ac5a564233bc..4be575b612b4 100644 --- a/internal/services/containers/kubernetes_cluster_resource.go +++ b/internal/services/containers/kubernetes_cluster_resource.go @@ -734,6 +734,189 @@ func resourceKubernetesCluster() *pluginsdk.Resource { }, }, + "maintenance_window_auto_upgrade": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "frequency": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Weekly", + "RelativeMonthly", + "AbsoluteMonthly", + }, false), + }, + + "interval": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "day_of_week": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + maintenanceconfigurations.PossibleValuesForWeekDay(), + false), + }, + + "duration": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(4, 24), + }, + + "week_index": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + maintenanceconfigurations.PossibleValuesForType(), + false), + }, + + "day_of_month": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 31), + }, + + "start_date": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + }, + + "start_time": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "utc_offset": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "not_allowed": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "end": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + + "start": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + }, + }, + }, + }, + }, + }, + + "maintenance_window_node_os": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "frequency": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Weekly", + "RelativeMonthly", + "AbsoluteMonthly", + "Daily", + }, false), + }, + + "interval": { + Type: pluginsdk.TypeInt, + Required: true, + }, + + "day_of_week": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + maintenanceconfigurations.PossibleValuesForWeekDay(), + false), + }, + + "duration": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(4, 24), + }, + + "week_index": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice( + maintenanceconfigurations.PossibleValuesForType(), + false), + }, + + "day_of_month": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 31), + }, + + "start_date": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + + "start_time": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "utc_offset": { + Type: pluginsdk.TypeString, + Optional: true, + }, + + "not_allowed": { + Type: pluginsdk.TypeSet, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "end": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + + "start": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.RFC3339Time, + ValidateFunc: validation.IsRFC3339Time, + }, + }, + }, + }, + }, + }, + }, + "node_os_channel_upgrade": { Type: pluginsdk.TypeString, Optional: true, @@ -1588,11 +1771,33 @@ func resourceKubernetesClusterCreate(d *pluginsdk.ResourceData, meta interface{} if maintenanceConfigRaw, ok := d.GetOk("maintenance_window"); ok { client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient parameters := maintenanceconfigurations.MaintenanceConfiguration{ - Properties: expandKubernetesClusterMaintenanceConfiguration(maintenanceConfigRaw.([]interface{})), + Properties: expandKubernetesClusterMaintenanceConfigurationDefault(maintenanceConfigRaw.([]interface{})), } maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "default") if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { - return fmt.Errorf("creating/updating maintenance config for %s: %+v", id, err) + return fmt.Errorf("creating/updating default maintenance config for %s: %+v", id, err) + } + } + + if maintenanceConfigRaw, ok := d.GetOk("maintenance_window_auto_upgrade"); ok { + client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient + parameters := maintenanceconfigurations.MaintenanceConfiguration{ + Properties: expandKubernetesClusterMaintenanceConfiguration(maintenanceConfigRaw.([]interface{})), + } + maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedAutoUpgradeSchedule") + if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { + return fmt.Errorf("creating/updating auto upgrade schedule maintenance config for %s: %+v", id, err) + } + } + + if maintenanceConfigRaw, ok := d.GetOk("maintenance_window_node_os"); ok { + client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient + parameters := maintenanceconfigurations.MaintenanceConfiguration{ + Properties: expandKubernetesClusterMaintenanceConfiguration(maintenanceConfigRaw.([]interface{})), + } + maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedNodeOSUpgradeSchedule") + if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { + return fmt.Errorf("creating/updating node os upgrade schedule maintenance config for %s: %+v", id, err) } } @@ -2201,12 +2406,55 @@ func resourceKubernetesClusterUpdate(d *pluginsdk.ResourceData, meta interface{} if d.HasChange("maintenance_window") { client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient - parameters := maintenanceconfigurations.MaintenanceConfiguration{ - Properties: expandKubernetesClusterMaintenanceConfiguration(d.Get("maintenance_window").([]interface{})), - } + maintenanceWindowProperties := expandKubernetesClusterMaintenanceConfigurationDefault(d.Get("maintenance_window").([]interface{})) maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "default") - if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { - return fmt.Errorf("creating/updating Maintenance Configuration for Managed Kubernetes Cluster (%q): %+v", id, err) + if maintenanceWindowProperties != nil { + parameters := maintenanceconfigurations.MaintenanceConfiguration{ + Properties: maintenanceWindowProperties, + } + if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { + return fmt.Errorf("creating/updating Maintenance Configuration for Managed Kubernetes Cluster (%q): %+v", id, err) + } + } else { + if _, err := client.Delete(ctx, maintenanceId); err != nil { + return fmt.Errorf("deleting Maintenance Configuration for %s: %+v", id, err) + } + } + } + + if d.HasChange("maintenance_window_auto_upgrade") { + client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient + maintenanceWindowProperties := expandKubernetesClusterMaintenanceConfiguration(d.Get("maintenance_window_auto_upgrade").([]interface{})) + maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedAutoUpgradeSchedule") + if maintenanceWindowProperties != nil { + parameters := maintenanceconfigurations.MaintenanceConfiguration{ + Properties: maintenanceWindowProperties, + } + if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { + return fmt.Errorf("creating/updating Auto Upgrade Schedule Maintenance Configuration for %s: %+v", id, err) + } + } else { + if _, err := client.Delete(ctx, maintenanceId); err != nil { + return fmt.Errorf("deleting Auto Upgrade Schedule Maintenance Configuration for %s: %+v", id, err) + } + } + } + + if d.HasChange("maintenance_window_node_os") { + client := meta.(*clients.Client).Containers.MaintenanceConfigurationsClient + maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedNodeOSUpgradeSchedule") + maintenanceWindowProperties := expandKubernetesClusterMaintenanceConfiguration(d.Get("maintenance_window_node_os").([]interface{})) + if maintenanceWindowProperties != nil { + parameters := maintenanceconfigurations.MaintenanceConfiguration{ + Properties: maintenanceWindowProperties, + } + if _, err := client.CreateOrUpdate(ctx, maintenanceId, parameters); err != nil { + return fmt.Errorf("creating/updating Node OS Upgrade Schedule Maintenance Configuration for %s: %+v", id, err) + } + } else { + if _, err := client.Delete(ctx, maintenanceId); err != nil { + return fmt.Errorf("deleting Node OS Upgrade Schedule Maintenance Configuration for %s: %+v", id, err) + } } } @@ -2504,7 +2752,19 @@ func resourceKubernetesClusterRead(d *pluginsdk.ResourceData, meta interface{}) maintenanceId := maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "default") configResp, _ := maintenanceConfigurationsClient.Get(ctx, maintenanceId) if configurationBody := configResp.Model; configurationBody != nil && configurationBody.Properties != nil { - d.Set("maintenance_window", flattenKubernetesClusterMaintenanceConfiguration(configurationBody.Properties)) + d.Set("maintenance_window", flattenKubernetesClusterMaintenanceConfigurationDefault(configurationBody.Properties)) + } + + maintenanceId = maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedAutoUpgradeSchedule") + configResp, _ = maintenanceConfigurationsClient.Get(ctx, maintenanceId) + if configurationBody := configResp.Model; configurationBody != nil && configurationBody.Properties != nil && configurationBody.Properties.MaintenanceWindow != nil { + d.Set("maintenance_window_auto_upgrade", flattenKubernetesClusterMaintenanceConfiguration(configurationBody.Properties.MaintenanceWindow)) + } + + maintenanceId = maintenanceconfigurations.NewMaintenanceConfigurationID(id.SubscriptionId, id.ResourceGroupName, id.ManagedClusterName, "aksManagedNodeOSUpgradeSchedule") + configResp, _ = maintenanceConfigurationsClient.Get(ctx, maintenanceId) + if configurationBody := configResp.Model; configurationBody != nil && configurationBody.Properties != nil && configurationBody.Properties.MaintenanceWindow != nil { + d.Set("maintenance_window_node_os", flattenKubernetesClusterMaintenanceConfiguration(configurationBody.Properties.MaintenanceWindow)) } if err := tags.FlattenAndSet(d, model.Tags); err != nil { @@ -3611,7 +3871,7 @@ func expandKubernetesClusterAzureKeyVaultKms(ctx context.Context, keyVaultsClien return azureKeyVaultKms, nil } -func expandKubernetesClusterMaintenanceConfiguration(input []interface{}) *maintenanceconfigurations.MaintenanceConfigurationProperties { +func expandKubernetesClusterMaintenanceConfigurationDefault(input []interface{}) *maintenanceconfigurations.MaintenanceConfigurationProperties { if len(input) == 0 { return nil } @@ -3622,6 +3882,68 @@ func expandKubernetesClusterMaintenanceConfiguration(input []interface{}) *maint } } +func expandKubernetesClusterMaintenanceConfiguration(input []interface{}) *maintenanceconfigurations.MaintenanceConfigurationProperties { + if len(input) == 0 { + return nil + } + value := input[0].(map[string]interface{}) + + var schedule maintenanceconfigurations.Schedule + + if value["frequency"] == "Daily" { + schedule = maintenanceconfigurations.Schedule{ + Daily: &maintenanceconfigurations.DailySchedule{ + IntervalDays: int64(value["interval"].(int)), + }, + } + } + if value["frequency"] == "Weekly" { + schedule = maintenanceconfigurations.Schedule{ + Weekly: &maintenanceconfigurations.WeeklySchedule{ + IntervalWeeks: int64(value["interval"].(int)), + DayOfWeek: maintenanceconfigurations.WeekDay(value["day_of_week"].(string)), + }, + } + } + if value["frequency"] == "AbsoluteMonthly" { + schedule = maintenanceconfigurations.Schedule{ + AbsoluteMonthly: &maintenanceconfigurations.AbsoluteMonthlySchedule{ + DayOfMonth: int64(value["day_of_month"].(int)), + IntervalMonths: int64(value["interval"].(int)), + }, + } + } + if value["frequency"] == "RelativeMonthly" { + schedule = maintenanceconfigurations.Schedule{ + RelativeMonthly: &maintenanceconfigurations.RelativeMonthlySchedule{ + DayOfWeek: maintenanceconfigurations.WeekDay(value["day_of_week"].(string)), + WeekIndex: maintenanceconfigurations.Type(value["week_index"].(string)), + IntervalMonths: int64(value["interval"].(int)), + }, + } + } + + output := &maintenanceconfigurations.MaintenanceConfigurationProperties{ + MaintenanceWindow: &maintenanceconfigurations.MaintenanceWindow{ + StartTime: value["start_time"].(string), + UtcOffset: utils.String(value["utc_offset"].(string)), + NotAllowedDates: expandKubernetesClusterMaintenanceConfigurationDateSpans(value["not_allowed"].(*pluginsdk.Set).List()), + Schedule: schedule, + }, + } + + if startDateRaw := value["start_date"]; startDateRaw != nil && startDateRaw.(string) != "" { + startDate, _ := time.Parse(time.RFC3339, startDateRaw.(string)) + output.MaintenanceWindow.StartDate = utils.String(startDate.Format("2006-01-02")) + } + + if duration := value["duration"]; duration != nil && duration.(int) != 0 { + output.MaintenanceWindow.DurationHours = int64(duration.(int)) + } + + return output +} + func expandKubernetesClusterMaintenanceConfigurationTimeSpans(input []interface{}) *[]maintenanceconfigurations.TimeSpan { results := make([]maintenanceconfigurations.TimeSpan, 0) for _, item := range input { @@ -3636,6 +3958,20 @@ func expandKubernetesClusterMaintenanceConfigurationTimeSpans(input []interface{ return &results } +func expandKubernetesClusterMaintenanceConfigurationDateSpans(input []interface{}) *[]maintenanceconfigurations.DateSpan { + results := make([]maintenanceconfigurations.DateSpan, 0) + for _, item := range input { + v := item.(map[string]interface{}) + start, _ := time.Parse(time.RFC3339, v["start"].(string)) + end, _ := time.Parse(time.RFC3339, v["end"].(string)) + results = append(results, maintenanceconfigurations.DateSpan{ + Start: start.Format("2006-01-02"), + End: end.Format("2006-01-02"), + }) + } + return &results +} + func expandKubernetesClusterMaintenanceConfigurationTimeInWeeks(input []interface{}) *[]maintenanceconfigurations.TimeInWeek { results := make([]maintenanceconfigurations.TimeInWeek, 0) for _, item := range input { @@ -3648,7 +3984,76 @@ func expandKubernetesClusterMaintenanceConfigurationTimeInWeeks(input []interfac return &results } -func flattenKubernetesClusterMaintenanceConfiguration(input *maintenanceconfigurations.MaintenanceConfigurationProperties) interface{} { +func flattenKubernetesClusterMaintenanceConfiguration(input *maintenanceconfigurations.MaintenanceWindow) interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + startDate := "" + if input.StartDate != nil { + startDate = *input.StartDate + "T00:00:00Z" + } + utcOfset := "" + if input.UtcOffset != nil { + utcOfset = *input.UtcOffset + } + + windowProperties := map[string]interface{}{ + "not_allowed": flattenKubernetesClusterMaintenanceConfigurationDateSpans(input.NotAllowedDates), + "duration": int(input.DurationHours), + "start_date": startDate, + "start_time": input.StartTime, + "utc_offset": utcOfset, + } + // Add flattened schedule properties + for k, v := range flattenKubernetesClusterMaintenanceConfigurationSchedule(input.Schedule) { + windowProperties[k] = v + } + + return append(results, windowProperties) +} + +func flattenKubernetesClusterMaintenanceConfigurationSchedule(input maintenanceconfigurations.Schedule) map[string]interface{} { + frequency := "" + interval := int64(0) + if input.Daily != nil { + frequency = "Daily" + interval = input.Daily.IntervalDays + } + + dayOfWeek := "" + if input.Weekly != nil { + frequency = "Weekly" + interval = input.Weekly.IntervalWeeks + dayOfWeek = string(input.Weekly.DayOfWeek) + } + + dayOfMonth := 0 + if input.AbsoluteMonthly != nil { + frequency = "AbsoluteMonthly" + interval = input.AbsoluteMonthly.IntervalMonths + dayOfMonth = int(input.AbsoluteMonthly.DayOfMonth) + } + + weekIndex := "" + if input.RelativeMonthly != nil { + frequency = "RelativeMonthly" + interval = input.RelativeMonthly.IntervalMonths + dayOfWeek = string(input.RelativeMonthly.DayOfWeek) + weekIndex = string(input.RelativeMonthly.WeekIndex) + } + + return map[string]interface{}{ + "frequency": frequency, + "interval": interval, + "day_of_week": dayOfWeek, + "week_index": weekIndex, + "day_of_month": dayOfMonth, + } +} + +func flattenKubernetesClusterMaintenanceConfigurationDefault(input *maintenanceconfigurations.MaintenanceConfigurationProperties) interface{} { results := make([]interface{}, 0) if input == nil { return results @@ -3683,6 +4088,29 @@ func flattenKubernetesClusterMaintenanceConfigurationTimeSpans(input *[]maintena return results } +func flattenKubernetesClusterMaintenanceConfigurationDateSpans(input *[]maintenanceconfigurations.DateSpan) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, item := range *input { + var end string + if item.End != "" { + end = item.End + } + var start string + if item.Start != "" { + start = item.Start + } + results = append(results, map[string]interface{}{ + "end": end + "T00:00:00Z", + "start": start + "T00:00:00Z", + }) + } + return results +} + func flattenKubernetesClusterMaintenanceConfigurationTimeInWeeks(input *[]maintenanceconfigurations.TimeInWeek) []interface{} { results := make([]interface{}, 0) if input == nil { diff --git a/website/docs/r/kubernetes_cluster.html.markdown b/website/docs/r/kubernetes_cluster.html.markdown index ed965ec74e04..b3bb00c64a9d 100644 --- a/website/docs/r/kubernetes_cluster.html.markdown +++ b/website/docs/r/kubernetes_cluster.html.markdown @@ -141,6 +141,10 @@ In addition, one of either `identity` or `service_principal` blocks must be spec * `maintenance_window` - (Optional) A `maintenance_window` block as defined below. +* `maintenance_window_auto_upgrade` - (Optional) A `maintenance_window_auto_upgrade` block as defined below. + +* `maintenance_window_node_os` - (Optional) A `maintenance_window_node_os` block as defined below. + * `microsoft_defender` - (Optional) A `microsoft_defender` block as defined below. * `monitor_metrics` - (Optional) Specifies a Prometheus add-on profile for the Kubernetes Cluster. A `monitor_metrics` block as defined below. @@ -572,6 +576,51 @@ A `maintenance_window` block supports the following: --- +A `maintenance_window_auto_upgrade` block supports the following: + +* `frequency` - (Required) Frequency of maintenance. Possible options are `Weekly`, `AbsoluteMonthly` and `RelativeMonthly`. + +* `interval` - (Required) The interval for maintenance runs. Depending on the frequency this interval is week or month based. + +* `duration` - (Required) The duration of the window for maintenance to run in hours. + +* `day_of_week` - (Optional) The day of the week for the maintenance run. Options are `Monday`, `Tuesday`, `Wednesday`, `Thurday`, `Friday`, `Saturday` and `Sunday`. Required in combination with weekly frequency. + +* `week_index` - (Optional) The week in the month used for the maintenance run. Options are `First`, `Second`, `Third`, `Fourth`, and `Last`. + Required in combination with relative monthly frequency. + +* `start_time` - (Optional) The time for maintenance to begin, based on the timezone determined by `utc_offset`. Format is `HH:mm`. + +* `utc_offset` - (Optional) Used to determine the timezone for cluster maintenance. + +* `start_date` - (Optional) The date on which the maintenance window begins to take effect. + +* `not_allowed` - (Optional) One or more `not_allowed` block as defined below. + +--- + +A `maintenance_window_node_os` block supports the following: + +* `frequency` - (Required) Frequency of maintenance. Possible options are `Daily`, `Weekly`, `AbsoluteMonthly` and `RelativeMonthly`. + +* `interval` - (Required) The interval for maintenance runs. Depending on the frequency this interval is week or month based. + +* `duration` - (Required) The duration of the window for maintenance to run in hours. + +* `day_of_week` - (Optional) The day of the week for the maintenance run. Options are `Monday`, `Tuesday`, `Wednesday`, `Thurday`, `Friday`, `Saturday` and `Sunday`. Required in combination with weekly frequency. + +* `week_index` - (Optional) The week in the month used for the maintenance run. Options are `First`, `Second`, `Third`, `Fourth`, and `Last`. + +* `start_time` - (Optional) The time for maintenance to begin, based on the timezone determined by `utc_offset`. Format is `HH:mm`. + +* `utc_offset` - (Optional) Used to determine the timezone for cluster maintenance. + +* `start_date` - (Optional) The date on which the maintenance window begins to take effect. + +* `not_allowed` - (Optional) One or more `not_allowed` block as defined below. + +--- + An `allowed` block exports the following: * `day` - (Required) A day in a week. Possible values are `Sunday`, `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday` and `Saturday`.