diff --git a/internal/services/automation/automation_software_update_configuration_resource.go b/internal/services/automation/automation_software_update_configuration_resource.go index 9d98a526ce3e..c08ff23d87fd 100644 --- a/internal/services/automation/automation_software_update_configuration_resource.go +++ b/internal/services/automation/automation_software_update_configuration_resource.go @@ -13,13 +13,34 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/automation/2021-06-22/automationaccount" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" validate4 "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" computeValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" validate2 "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" - "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +const ( + RebootSettingIfRequired = "IfRequired" + RebootSettingNever = "Never" + RebootSettingAlways = "Always" + RebootSettingRebootOnly = "RebootOnly" + + FrequencyOneTime = "OneTime" + FrequencyDay = "Day" + FrequencyHour = "Hour" + FrequencyWeek = "Week" + FrequencyMonth = "Month" + + DaysOfWeekMonday = "Monday" + DaysOfWeekTuesday = "Tuesday" + DaysOfWeekWednesday = "Wednesday" + DaysOfWeekThursday = "Thursday" + DaysOfWeekFriday = "Friday" + DaysOfWeekSaturday = "Saturday" + DaysOfWeekSunday = "Sunday" ) type Tag struct { @@ -34,38 +55,17 @@ type AzureQuery struct { TagFilter string `tfschema:"tag_filter"` } -func (a *AzureQuery) LoadSDKTags(tags map[string][]string) { - if tags == nil { - return - } - for k, vs := range tags { - t := Tag{} - t.Tag = k - t.Values = append(t.Values, vs...) - a.Tags = append(a.Tags, t) - } -} -func (a *AzureQuery) ToSDKTags() *map[string][]string { - m := map[string][]string{} - if len(a.Tags) == 0 { - // return an empty map instead of nil until issue fixed: https://github.com/Azure/azure-rest-api-specs/issues/21719 - return &m - } - for _, tag := range a.Tags { - m[tag.Tag] = tag.Values - } - return &m -} - type Linux struct { Reboot string `tfschema:"reboot"` - Classification string `tfschema:"classification_included"` + Classifications []string `tfschema:"classifications_included"` ExcludedPackages []string `tfschema:"excluded_packages"` IncludedPackages []string `tfschema:"included_packages"` + + Classification string `tfschema:"classification_included"` // Deprecated use Classifications instead } type MonthlyOccurrence struct { - Occurrence int64 `tfschema:"occurrence"` + Occurrence int `tfschema:"occurrence"` Day string `tfschema:"day"` } @@ -79,40 +79,6 @@ type UpdateTask struct { Parameters map[string]string `tfschema:"parameters"` } -func updateTaskFromSDK(prop *softwareupdateconfiguration.TaskProperties) (res []UpdateTask) { - if prop == nil { - return - } - res = append(res, UpdateTask{ - Source: utils.NormalizeNilableString(prop.Source), - }) - - if prop.Parameters != nil { - res[0].Parameters = map[string]string{} - for k, v := range *prop.Parameters { - res[0].Parameters[k] = v - } - } - return -} - -func (u *UpdateTask) ToSDKModel() *softwareupdateconfiguration.TaskProperties { - if u == nil { - return nil - } - res := &softwareupdateconfiguration.TaskProperties{ - Source: utils.String(u.Source), - } - - params := make(map[string]string) - for k, v := range u.Parameters { - vCopy := v - params[k] = vCopy - } - res.Parameters = ¶ms - return res -} - type Schedule struct { Description string `tfschema:"description"` StartTime string `tfschema:"start_time"` @@ -132,200 +98,19 @@ type Schedule struct { MonthlyOccurrence []MonthlyOccurrence `tfschema:"monthly_occurrence"` } -func (s *Schedule) LoadSDKModel(info *softwareupdateconfiguration.SUCScheduleProperties) { - - startTime := "" - if info.StartTime != nil { - startTime = *info.StartTime - } - - expiryTime := "" - if info.ExpiryTime != nil { - expiryTime = *info.ExpiryTime - } - - nextRun := "" - if info.NextRun != nil { - nextRun = *info.NextRun - } - - creationTime := "" - if info.CreationTime != nil { - creationTime = *info.CreationTime - } - - lastModifiedTime := "" - if info.LastModifiedTime != nil { - lastModifiedTime = *info.LastModifiedTime - } - - s.StartTime = startTime - s.StartTimeOffsetMinutes = pointer.ToFloat64(info.StartTimeOffsetMinutes) - s.ExpiryTime = expiryTime - s.ExpiryTimeOffsetMinutes = pointer.ToFloat64(info.ExpiryTimeOffsetMinutes) - s.IsEnabled = utils.NormaliseNilableBool(info.IsEnabled) - s.NextRun = nextRun - s.NextRunOffsetMinutes = pointer.ToFloat64(info.NextRunOffsetMinutes) - if info.Interval != nil { - s.Interval = int(*info.Interval) - } - if info.Frequency != nil { - s.Frequency = string(*info.Frequency) - } - - s.TimeZone = utils.NormalizeNilableString(info.TimeZone) - s.CreationTime = creationTime - s.LastModifiedTime = lastModifiedTime - s.Description = utils.NormalizeNilableString(info.Description) - - if setting := info.AdvancedSchedule; setting != nil { - s.AdvancedWeekDays = pointer.ToSliceOfStrings(setting.WeekDays) - if setting.MonthDays != nil { - for _, v := range *(setting.MonthDays) { - s.AdvancedMonthDays = append(s.AdvancedMonthDays, int(v)) - } - } - - if setting.MonthlyOccurrences != nil { - for _, occ := range *setting.MonthlyOccurrences { - - day := "" - if occ.Day != nil { - day = string(*occ.Day) - } - - s.MonthlyOccurrence = append(s.MonthlyOccurrence, MonthlyOccurrence{ - Occurrence: utils.NormaliseNilableInt64(occ.Occurrence), - Day: day, - }) - } - } - } -} - -// will keep old values encode from config -func scheduleFromSDK(info softwareupdateconfiguration.SUCScheduleProperties, old []Schedule) []Schedule { - if len(old) == 0 { - old = append(old, Schedule{}) - } - old[0].LoadSDKModel(&info) - - return old -} - -func (s *Schedule) ToSDKModel() softwareupdateconfiguration.SUCScheduleProperties { - if s == nil { - return softwareupdateconfiguration.SUCScheduleProperties{} - } - timeZone := s.TimeZone - - frequency := softwareupdateconfiguration.ScheduleFrequency(s.Frequency) - res := softwareupdateconfiguration.SUCScheduleProperties{ - StartTimeOffsetMinutes: utils.Float(s.StartTimeOffsetMinutes), - ExpiryTimeOffsetMinutes: utils.Float(s.ExpiryTimeOffsetMinutes), - IsEnabled: utils.Bool(s.IsEnabled), - NextRun: &s.NextRun, - NextRunOffsetMinutes: utils.Float(s.NextRunOffsetMinutes), - Interval: utils.Int64(int64(s.Interval)), - TimeZone: &timeZone, - AdvancedSchedule: &softwareupdateconfiguration.AdvancedSchedule{}, - Description: utils.String(s.Description), - Frequency: &frequency, - } - - loc, _ := time.LoadLocation(timeZone) - if s.StartTime != "" { - startTime, _ := time.Parse(time.RFC3339, s.StartTime) - res.SetStartTimeAsTime(startTime.In(loc)) - } else { - res.StartTime = &s.StartTime - } - if s.ExpiryTime != "" { - expiryTime, _ := time.Parse(time.RFC3339, s.ExpiryTime) - res.SetExpiryTimeAsTime(expiryTime.In(loc)) - } else { - res.ExpiryTime = &s.ExpiryTime - } - - if len(s.AdvancedWeekDays) > 0 { - res.AdvancedSchedule.WeekDays = &s.AdvancedWeekDays - } - - if len(s.AdvancedMonthDays) > 0 { - var is []int64 - for _, v := range s.AdvancedMonthDays { - is = append(is, int64(v)) - } - res.AdvancedSchedule.MonthDays = &is - } - - var occ []softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence - for _, m := range s.MonthlyOccurrence { - - day := softwareupdateconfiguration.ScheduleDay(m.Day) - occ = append(occ, softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence{ - Occurrence: utils.Int64(m.Occurrence), - Day: &day, - }) - } - - if len(occ) > 0 { - res.AdvancedSchedule.MonthlyOccurrences = &occ - } - return res -} - type Target struct { AzureQueries []AzureQuery `tfschema:"azure_query"` NonAzureQueries []NonAzureQuery `tfschema:"non_azure_query"` } -func targetsFromSDK(prop *softwareupdateconfiguration.TargetProperties) []Target { - if prop == nil { - return nil - } - - var t Target - if prop.AzureQueries != nil { - for _, az := range *prop.AzureQueries { - q := AzureQuery{ - Scope: pointer.ToSliceOfStrings(az.Scope), - Locations: pointer.ToSliceOfStrings(az.Locations), - } - if setting := az.TagSettings; setting != nil { - if setting.Tags != nil { - q.LoadSDKTags(*setting.Tags) - } - - if setting.FilterOperator != nil { - q.TagFilter = string(*setting.FilterOperator) - } - } - t.AzureQueries = append(t.AzureQueries, q) - } - } - - if prop.NonAzureQueries != nil { - for _, az := range *prop.NonAzureQueries { - q := NonAzureQuery{ - FunctionAlias: utils.NormalizeNilableString(az.FunctionAlias), - WorkspaceId: utils.NormalizeNilableString(az.WorkspaceId), - } - t.NonAzureQueries = append(t.NonAzureQueries, q) - } - } - - return []Target{t} -} - type Windows struct { - // Classification Deprecated, use Classifications instead - Classification string `tfschema:"classification_included"` - Classifications []string `tfschema:"classifications_included"` ExcludedKbs []string `tfschema:"excluded_knowledge_base_numbers"` IncludedKbs []string `tfschema:"included_knowledge_base_numbers"` RebootSetting string `tfschema:"reboot"` + + Classification string `tfschema:"classification_included"` // Deprecated use Classifications instead + } type SoftwareUpdateConfigurationModel struct { @@ -346,179 +131,220 @@ type SoftwareUpdateConfigurationModel struct { PostTask []UpdateTask `tfschema:"post_task"` } -func (s *SoftwareUpdateConfigurationModel) ToSDKModel() softwareupdateconfiguration.SoftwareUpdateConfiguration { - var param softwareupdateconfiguration.SoftwareUpdateConfiguration - param.Name = utils.String(s.Name) - upd := softwareupdateconfiguration.UpdateConfiguration{} - upd.OperatingSystem = softwareupdateconfiguration.OperatingSystemType(s.OperatingSystem) - - if len(s.Linux) > 0 { - l := s.Linux[0] - linuxUpdateClasses := softwareupdateconfiguration.LinuxUpdateClasses(l.Classification) - upd.Linux = &softwareupdateconfiguration.LinuxProperties{ - IncludedPackageClassifications: &linuxUpdateClasses, - } - if l.Reboot != "" { - upd.Linux.RebootSetting = utils.String(l.Reboot) - } - - upd.Linux.IncludedPackageNameMasks = utils.StringSlice(l.IncludedPackages) - upd.Linux.ExcludedPackageNameMasks = utils.StringSlice(l.ExcludedPackages) - } +type SoftwareUpdateConfigurationResource struct{} - if len(s.Windows) > 0 { - w := s.Windows[0] - includedUpdateClassifications := softwareupdateconfiguration.WindowsUpdateClasses(strings.Join(w.Classifications, ",")) - upd.Windows = &softwareupdateconfiguration.WindowsProperties{ - IncludedUpdateClassifications: &includedUpdateClassifications, - } - if len(w.Classifications) == 0 && w.Classification != "" { - includedUpdateClassifications = softwareupdateconfiguration.WindowsUpdateClasses(w.Classification) - upd.Windows.IncludedUpdateClassifications = &includedUpdateClassifications - } +var _ sdk.ResourceWithUpdate = SoftwareUpdateConfigurationResource{} - if w.RebootSetting != "" { - upd.Windows.RebootSetting = utils.String(w.RebootSetting) - } - upd.Windows.IncludedKbNumbers = utils.StringSlice(w.IncludedKbs) - upd.Windows.ExcludedKbNumbers = utils.StringSlice(w.ExcludedKbs) - } +func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.Schema { + linux := pluginsdk.Resource{} + windows := pluginsdk.Resource{} + if !features.FourPointOhBeta() { + linux = pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "reboot": { + Type: pluginsdk.TypeString, + Optional: true, + Default: RebootSettingIfRequired, + ValidateFunc: validation.StringInSlice([]string{ + RebootSettingAlways, + RebootSettingIfRequired, + RebootSettingNever, + RebootSettingRebootOnly, + }, false), + }, - upd.Duration = utils.String(s.Duration) - upd.AzureVirtualMachines = utils.StringSlice(s.VirtualMachines) - upd.NonAzureComputerNames = utils.StringSlice(s.NonAzureComputerNames) - - if len(s.Targets) > 0 { - upd.Targets = &softwareupdateconfiguration.TargetProperties{} - var azureQueries []softwareupdateconfiguration.AzureQueryProperties - t := s.Targets[0] - for _, az := range t.AzureQueries { - q := softwareupdateconfiguration.AzureQueryProperties{ - Scope: utils.StringSlice(az.Scope), - Locations: utils.StringSlice(az.Locations), - TagSettings: nil, - } - tag := softwareupdateconfiguration.TagSettingsProperties{} - tag.Tags = az.ToSDKTags() - // always set filterOperator until issue fixed: https://github.com/Azure/azure-rest-api-specs/issues/21719 - filterOperator := softwareupdateconfiguration.TagOperatorsAll - tag.FilterOperator = &filterOperator - if az.TagFilter != "" { - tagOperators := softwareupdateconfiguration.TagOperators(az.TagFilter) - tag.FilterOperator = &tagOperators - } - q.TagSettings = &tag - azureQueries = append(azureQueries, q) - } + "classification_included": { + Type: pluginsdk.TypeString, + Optional: true, + ConflictsWith: []string{"windows.0.classifications_included"}, + Computed: true, + ValidateFunc: validation.StringInSlice( + softwareupdateconfiguration.PossibleValuesForLinuxUpdateClasses(), + false), + Deprecated: "", // TODO + }, - if azureQueries != nil { - upd.Targets.AzureQueries = &azureQueries - } + "classifications_included": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + ConflictsWith: []string{"windows.0.classification_included"}, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + softwareupdateconfiguration.PossibleValuesForLinuxUpdateClasses(), + false), + }, + }, - var nonAzureQueries []softwareupdateconfiguration.NonAzureQueryProperties - for _, az := range t.NonAzureQueries { - q := softwareupdateconfiguration.NonAzureQueryProperties{ - FunctionAlias: utils.String(az.FunctionAlias), - WorkspaceId: utils.String(az.WorkspaceId), - } - nonAzureQueries = append(nonAzureQueries, q) - } + "excluded_packages": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - if nonAzureQueries != nil { - upd.Targets.NonAzureQueries = &nonAzureQueries + "included_packages": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, } - } - - prop := softwareupdateconfiguration.SoftwareUpdateConfigurationProperties{} - - if len(s.Schedule) > 0 { - prop.ScheduleInfo = s.Schedule[0].ToSDKModel() - } - - tasks := softwareupdateconfiguration.SoftwareUpdateConfigurationTasks{} - if len(s.PreTask) > 0 { - tasks.PreTask = s.PreTask[0].ToSDKModel() - } - if len(s.PostTask) > 0 { - tasks.PostTask = s.PostTask[0].ToSDKModel() - } - prop.Tasks = &tasks - prop.UpdateConfiguration = upd - param.Properties = prop - return param -} - -func (s *SoftwareUpdateConfigurationModel) LoadSDKModel(model *softwareupdateconfiguration.SoftwareUpdateConfiguration) { - if model == nil { - return - } - props := model.Properties + windows = pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "classification_included": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"windows.0.classifications_included"}, + Deprecated: "windows classification can be set as a list, use `classifications_included` instead.", + ValidateFunc: validation.StringInSlice(func() (vs []string) { + vs = append(vs, softwareupdateconfiguration.PossibleValuesForWindowsUpdateClasses()...) + return + }(), false), + }, - if props.Error != nil { - s.ErrorCode = utils.NormalizeNilableString(props.Error.Code) - s.ErrorMeesage = utils.NormalizeNilableString(props.Error.Message) - s.ErrorMessage = utils.NormalizeNilableString(props.Error.Message) - } + "classifications_included": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + ConflictsWith: []string{"windows.0.classification_included"}, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(func() (res []string) { + res = append(res, softwareupdateconfiguration.PossibleValuesForWindowsUpdateClasses()...) + return + }(), false), + }, + }, - conf := props.UpdateConfiguration - s.OperatingSystem = string(conf.OperatingSystem) + "excluded_knowledge_base_numbers": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - if l := conf.Linux; l != nil { + "included_knowledge_base_numbers": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - includedPackageClassifications := "" - if l.IncludedPackageClassifications != nil { - includedPackageClassifications = string(*l.IncludedPackageClassifications) + "reboot": { + Type: pluginsdk.TypeString, + Optional: true, + Default: RebootSettingIfRequired, + ValidateFunc: validation.StringInSlice([]string{ + RebootSettingAlways, + RebootSettingIfRequired, + RebootSettingNever, + RebootSettingRebootOnly, + }, false), + }, + }, } + } else { + linux = pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "reboot": { + Type: pluginsdk.TypeString, + Optional: true, + Default: RebootSettingIfRequired, + ValidateFunc: validation.StringInSlice([]string{ + RebootSettingAlways, + RebootSettingIfRequired, + RebootSettingNever, + RebootSettingRebootOnly, + }, false), + }, - s.Linux = []Linux{{ - Reboot: utils.NormalizeNilableString(l.RebootSetting), - Classification: includedPackageClassifications, - ExcludedPackages: pointer.ToSliceOfStrings(l.ExcludedPackageNameMasks), - IncludedPackages: pointer.ToSliceOfStrings(l.IncludedPackageNameMasks), - }} - } - - if w := conf.Windows; w != nil { + "classifications_included": { + Type: pluginsdk.TypeList, + Required: true, // TODO 4.0 - Update docs to reflect this breaking change. + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(softwareupdateconfiguration.PossibleValuesForLinuxUpdateClasses(), + false), + }, + }, - classification := "" - if w.IncludedUpdateClassifications != nil { - classification = string(*w.IncludedUpdateClassifications) - } + "excluded_packages": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - s.Windows = []Windows{ - { - Classification: classification, - ExcludedKbs: pointer.ToSliceOfStrings(w.ExcludedKbNumbers), - IncludedKbs: pointer.ToSliceOfStrings(w.IncludedKbNumbers), - RebootSetting: utils.NormalizeNilableString(w.RebootSetting), + "included_packages": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, }, } + windows = pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "classifications_included": { + Type: pluginsdk.TypeList, + Required: true, // TODO 4.0 - Update docs to reflect this breaking change. + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice( + softwareupdateconfiguration.PossibleValuesForWindowsUpdateClasses(), + false), + }, + }, - for _, v := range strings.Split(classification, ",") { - s.Windows[0].Classifications = append(s.Windows[0].Classifications, strings.TrimSpace(v)) - } - } + "excluded_knowledge_base_numbers": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - s.Duration = utils.NormalizeNilableString(conf.Duration) - s.VirtualMachines = pointer.ToSliceOfStrings(conf.AzureVirtualMachines) - s.NonAzureComputerNames = pointer.ToSliceOfStrings(conf.NonAzureComputerNames) - s.Targets = targetsFromSDK(conf.Targets) + "included_knowledge_base_numbers": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, - // service api response scheduleInfo.advancedSchedule as null, which cause import lost it - s.Schedule = scheduleFromSDK(props.ScheduleInfo, s.Schedule) - if tasks := props.Tasks; tasks != nil { - s.PreTask = updateTaskFromSDK(tasks.PreTask) - s.PostTask = updateTaskFromSDK(tasks.PostTask) + "reboot": { + Type: pluginsdk.TypeString, + Optional: true, + Default: RebootSettingIfRequired, + ValidateFunc: validation.StringInSlice([]string{ + RebootSettingAlways, + RebootSettingIfRequired, + RebootSettingNever, + RebootSettingRebootOnly, + }, false), + }, + }, + } } -} - -type SoftwareUpdateConfigurationResource struct{} - -var _ sdk.ResourceWithUpdate = (*SoftwareUpdateConfigurationResource)(nil) - -func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ - + r := map[string]*pluginsdk.Schema{ "automation_account_id": { Type: pluginsdk.TypeString, Required: true, @@ -533,53 +359,14 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S ValidateFunc: validation.StringIsNotEmpty, }, - "operating_system": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(softwareupdateconfiguration.OperatingSystemTypeLinux), - string(softwareupdateconfiguration.OperatingSystemTypeWindows), - }, false), - }, - "linux": { Type: pluginsdk.TypeList, Optional: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - - "reboot": { - Type: pluginsdk.TypeString, - Optional: true, - }, - - "classification_included": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringInSlice(func() (vs []string) { - vs = append(vs, softwareupdateconfiguration.PossibleValuesForLinuxUpdateClasses()...) - return - }(), false), - }, - - "excluded_packages": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - - "included_packages": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - }, + MaxItems: 1, + Elem: &linux, + ExactlyOneOf: []string{ + "windows", + "linux", }, }, @@ -587,66 +374,17 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Optional: true, MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - - "classification_included": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ConflictsWith: []string{"windows.0.classifications_included"}, - AtLeastOneOf: []string{"windows.0.classification_included", "windows.0.classifications_included"}, - Deprecated: "windows classification can be set as a list, use `classifications_included` instead.", - ValidateFunc: validation.StringInSlice(func() (vs []string) { - vs = append(vs, softwareupdateconfiguration.PossibleValuesForWindowsUpdateClasses()...) - return - }(), false), - }, - - "classifications_included": { - Type: pluginsdk.TypeList, - Optional: true, - Computed: true, - ConflictsWith: []string{"windows.0.classification_included"}, - AtLeastOneOf: []string{"windows.0.classification_included", "windows.0.classifications_included"}, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringInSlice(func() (res []string) { - res = append(res, softwareupdateconfiguration.PossibleValuesForWindowsUpdateClasses()...) - return - }(), false), - }, - }, - - "excluded_knowledge_base_numbers": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - - "included_knowledge_base_numbers": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - - "reboot": { - Type: pluginsdk.TypeString, - Optional: true, - }, - }, + Elem: &windows, + ExactlyOneOf: []string{ + "windows", + "linux", }, }, "duration": { Type: pluginsdk.TypeString, Optional: true, + Default: "PT2H", ValidateFunc: validate4.ISO8601Duration, }, @@ -674,7 +412,6 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "azure_query": { Type: pluginsdk.TypeList, Optional: true, @@ -764,7 +501,8 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "schedule": { Type: pluginsdk.TypeList, - Optional: true, + Required: true, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "description": { @@ -775,6 +513,7 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "start_time": { Type: pluginsdk.TypeString, Optional: true, + Computed: true, DiffSuppressFunc: suppress.RFC3339MinuteTime, ValidateFunc: validation.IsRFC3339Time, }, @@ -802,6 +541,7 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "is_enabled": { Type: pluginsdk.TypeBool, Optional: true, + Default: true, }, "next_run": { @@ -825,7 +565,14 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "frequency": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + FrequencyOneTime, + FrequencyHour, + FrequencyDay, + FrequencyWeek, + FrequencyMonth, + }, false), }, "creation_time": { @@ -849,8 +596,16 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Optional: true, Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: nil, + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + DaysOfWeekMonday, + DaysOfWeekTuesday, + DaysOfWeekWednesday, + DaysOfWeekThursday, + DaysOfWeekFriday, + DaysOfWeekSaturday, + DaysOfWeekSunday, + }, false), }, }, @@ -866,6 +621,7 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "monthly_occurrence": { Type: pluginsdk.TypeList, Optional: true, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "occurrence": { @@ -895,6 +651,7 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "pre_task": { Type: pluginsdk.TypeList, Optional: true, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "source": { @@ -918,9 +675,9 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S "post_task": { Type: pluginsdk.TypeList, Optional: true, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "source": { Type: pluginsdk.TypeString, Optional: true, @@ -939,6 +696,21 @@ func (m SoftwareUpdateConfigurationResource) Arguments() map[string]*pluginsdk.S }, }, } + + if !features.FourPointOhBeta() { + r["operating_system"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + string(softwareupdateconfiguration.OperatingSystemTypeLinux), + string(softwareupdateconfiguration.OperatingSystemTypeWindows), + }, false), + Deprecated: "This property has been deprecated and will be removed in a future release. The use of either the `linux` or `windows` blocks replaces setting this value directly. This value is ignored by the provider.", + } + } + + return r } func (m SoftwareUpdateConfigurationResource) Attributes() map[string]*pluginsdk.Schema { @@ -980,6 +752,7 @@ func (m SoftwareUpdateConfigurationResource) Create() sdk.ResourceFunc { if err := meta.Decode(&model); err != nil { return err } + automationID, err := automationaccount.ParseAutomationAccountID(model.AutomationAccountID) if err != nil { return err @@ -988,19 +761,14 @@ func (m SoftwareUpdateConfigurationResource) Create() sdk.ResourceFunc { subscriptionID := meta.Client.Account.SubscriptionId id := softwareupdateconfiguration.NewSoftwareUpdateConfigurationID(subscriptionID, automationID.ResourceGroupName, automationID.AutomationAccountName, model.Name) existing, err := client.SoftwareUpdateConfigurationsGetByName(ctx, id, softwareupdateconfiguration.SoftwareUpdateConfigurationsGetByNameOperationOptions{}) - if !response.WasNotFound(existing.HttpResponse) { - if err != nil { - return fmt.Errorf("retrieving %s: %v", id, err) - } - if meta.ResourceData.IsNewResource() { + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { return meta.ResourceRequiresImport(m.ResourceType(), id) } } - param := model.ToSDKModel() - _, err = client.SoftwareUpdateConfigurationsCreate(ctx, id, param, softwareupdateconfiguration.SoftwareUpdateConfigurationsCreateOperationOptions{}) - - if err != nil { + updateConfig := expandUpdateConfig(model) + if _, err = client.SoftwareUpdateConfigurationsCreate(ctx, id, *updateConfig, softwareupdateconfiguration.SoftwareUpdateConfigurationsCreateOperationOptions{}); err != nil { return fmt.Errorf("creating %s: %v", id, err) } @@ -1026,24 +794,386 @@ func (m SoftwareUpdateConfigurationResource) Read() sdk.ResourceFunc { } return err } - var output SoftwareUpdateConfigurationModel - if err := meta.Decode(&output); err != nil { - return err + + var state SoftwareUpdateConfigurationModel + + state.AutomationAccountID = softwareupdateconfiguration.NewAutomationAccountID(id.SubscriptionId, id.ResourceGroupName, id.AutomationAccountName).ID() + + props := resp.Model.Properties + updateConfiguration := props.UpdateConfiguration + scheduleConfiguration := props.ScheduleInfo + + state.Name = id.SoftwareUpdateConfigurationName + state.Duration = pointer.From(updateConfiguration.Duration) + if linux := updateConfiguration.Linux; linux != nil { + l := Linux{ + Reboot: pointer.From(linux.RebootSetting), + Classifications: strings.Split(string(pointer.From(linux.IncludedPackageClassifications)), ", "), + ExcludedPackages: pointer.From(linux.ExcludedPackageNameMasks), + IncludedPackages: pointer.From(linux.IncludedPackageNameMasks), + } + + if !features.FourPointOhBeta() && len(strings.Split(string(pointer.From(linux.IncludedPackageClassifications)), ", ")) == 1 { + l.Classification = string(pointer.From(linux.IncludedPackageClassifications)) + } + + state.Linux = []Linux{l} + state.OperatingSystem = string(softwareupdateconfiguration.OperatingSystemTypeLinux) } + if windows := updateConfiguration.Windows; windows != nil { + w := Windows{ + Classifications: strings.Split(strings.ReplaceAll(string(pointer.From(windows.IncludedUpdateClassifications)), " ", ""), ","), + ExcludedKbs: pointer.From(windows.ExcludedKbNumbers), + IncludedKbs: pointer.From(windows.IncludedKbNumbers), + RebootSetting: pointer.From(windows.RebootSetting), + } + + if !features.FourPointOhBeta() && len(strings.Split(string(pointer.From(windows.IncludedUpdateClassifications)), ", ")) == 1 { + w.Classification = string(pointer.From(windows.IncludedUpdateClassifications)) + } + + state.Windows = []Windows{w} + state.OperatingSystem = string(softwareupdateconfiguration.OperatingSystemTypeWindows) + } + if targets := updateConfiguration.Targets; targets != nil { + t := Target{} + aq := make([]AzureQuery, 0) + for _, v := range pointer.From(targets.AzureQueries) { + tagsList := make([]Tag, 0) + tagFilter := "" + if tags := v.TagSettings; tags != nil { + for k, vals := range pointer.From(tags.Tags) { + tagsList = append(tagsList, Tag{ + Tag: k, + Values: vals, + }) + } + tagFilter = string(pointer.From(tags.FilterOperator)) + } + aq = append(aq, AzureQuery{ + Scope: pointer.From(v.Scope), + Locations: pointer.From(v.Locations), + Tags: tagsList, + TagFilter: tagFilter, + }) + } - output.Name = id.SoftwareUpdateConfigurationName - output.AutomationAccountID = softwareupdateconfiguration.NewAutomationAccountID(id.SubscriptionId, id.ResourceGroupName, id.AutomationAccountName).ID() - output.LoadSDKModel(resp.Model) + t.AzureQueries = aq - return meta.Encode(&output) + naq := make([]NonAzureQuery, 0) + for _, v := range pointer.From(targets.NonAzureQueries) { + naq = append(naq, NonAzureQuery{ + FunctionAlias: pointer.From(v.FunctionAlias), + WorkspaceId: pointer.From(v.WorkspaceId), + }) + } + + t.NonAzureQueries = naq + state.Targets = []Target{t} + } + + state.VirtualMachines = pointer.From(updateConfiguration.AzureVirtualMachines) + state.NonAzureComputerNames = pointer.From(updateConfiguration.NonAzureComputerNames) + + schedule := Schedule{ + Description: pointer.From(scheduleConfiguration.Description), + StartTime: pointer.From(scheduleConfiguration.StartTime), + StartTimeOffsetMinutes: pointer.From(scheduleConfiguration.StartTimeOffsetMinutes), + ExpiryTime: pointer.From(scheduleConfiguration.ExpiryTime), + ExpiryTimeOffsetMinutes: pointer.From(scheduleConfiguration.ExpiryTimeOffsetMinutes), + IsEnabled: pointer.From(scheduleConfiguration.IsEnabled), + NextRun: pointer.From(scheduleConfiguration.NextRun), + NextRunOffsetMinutes: pointer.From(scheduleConfiguration.NextRunOffsetMinutes), + Interval: int(pointer.From(scheduleConfiguration.Interval)), + Frequency: string(pointer.From(scheduleConfiguration.Frequency)), + CreationTime: pointer.From(scheduleConfiguration.CreationTime), + LastModifiedTime: pointer.From(scheduleConfiguration.LastModifiedTime), + TimeZone: pointer.From(scheduleConfiguration.TimeZone), + } + + // (@jackofallops) - Advanced Schedule info is never returned so we'll pull it in from Config until the tracked issue is resolved + // Tracking Issue: https://github.com/Azure/azure-rest-api-specs/issues/24436 + if advSchedule := scheduleConfiguration.AdvancedSchedule; advSchedule != nil { + schedule.AdvancedWeekDays = pointer.From(advSchedule.WeekDays) + if monthDays := pointer.From(advSchedule.MonthDays); len(monthDays) > 0 { + advMonthDays := make([]int, 0) + for _, v := range monthDays { + advMonthDays = append(advMonthDays, int(v)) + } + schedule.AdvancedMonthDays = advMonthDays + } + if monthlyOccurrence := pointer.From(advSchedule.MonthlyOccurrences); len(monthlyOccurrence) > 0 { + mo := make([]MonthlyOccurrence, 0) + for _, v := range monthlyOccurrence { + mo = append(mo, MonthlyOccurrence{ + Occurrence: int(pointer.From(v.Occurrence)), + Day: string(pointer.From(v.Day)), + }) + } + schedule.MonthlyOccurrence = mo + } + } else { + if weekDays, ok := meta.ResourceData.GetOk("schedule.0.advanced_week_days"); ok { + wd := make([]string, 0) + for _, v := range weekDays.([]interface{}) { + wd = append(wd, v.(string)) + } + schedule.AdvancedWeekDays = wd + } + if monthDays, ok := meta.ResourceData.GetOk("schedule.0.advanced_month_days"); ok { + md := make([]int, 0) + for _, v := range monthDays.([]interface{}) { + md = append(md, v.(int)) + } + schedule.AdvancedMonthDays = md + } + if monthlyOccurrence, ok := meta.ResourceData.GetOk("schedule.0.monthly_occurrence"); ok { + mos := make([]MonthlyOccurrence, 0) + if moRaw, ok := monthlyOccurrence.([]interface{}); ok { + for _, v := range moRaw { + mo := v.(map[string]interface{}) + mos = append(mos, MonthlyOccurrence{ + Occurrence: mo["occurrence"].(int), + Day: mo["day"].(string), + }) + } + } + schedule.MonthlyOccurrence = mos + } + } + + state.Schedule = []Schedule{schedule} + + if tasks := props.Tasks; tasks != nil { + if pre := tasks.PreTask; pre != nil { + state.PreTask = []UpdateTask{{ + Source: pointer.From(pre.Source), + Parameters: pointer.From(pre.Parameters), + }} + } + if post := tasks.PostTask; post != nil { + state.PostTask = []UpdateTask{{ + Source: pointer.From(post.Source), + Parameters: pointer.From(post.Parameters), + }} + } + } + + return meta.Encode(&state) }, } } func (m SoftwareUpdateConfigurationResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ - Func: m.Create().Func, - Timeout: 10 * time.Minute, + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.Automation.SoftwareUpdateConfigClient + + id, err := softwareupdateconfiguration.ParseSoftwareUpdateConfigurationID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model SoftwareUpdateConfigurationModel + if err = metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.SoftwareUpdateConfigurationsGetByName(ctx, *id, softwareupdateconfiguration.SoftwareUpdateConfigurationsGetByNameOperationOptions{}) + if err != nil { + return fmt.Errorf("reading %s: %+v", *id, err) + } + + existing := resp.Model + + if metadata.ResourceData.HasChange("linux") { + if len(model.Linux) > 0 { + existing.Properties.UpdateConfiguration.OperatingSystem = softwareupdateconfiguration.OperatingSystemTypeLinux + v := model.Linux[0] + existing.Properties.UpdateConfiguration.Linux = &softwareupdateconfiguration.LinuxProperties{ + ExcludedPackageNameMasks: pointer.To(v.ExcludedPackages), + IncludedPackageClassifications: pointer.To(softwareupdateconfiguration.LinuxUpdateClasses(strings.Join(v.Classifications, ","))), + IncludedPackageNameMasks: pointer.To(v.IncludedPackages), + RebootSetting: pointer.To(v.Reboot), + } + } else { + existing.Properties.UpdateConfiguration.Linux = &softwareupdateconfiguration.LinuxProperties{} + } + } + + if metadata.ResourceData.HasChange("windows") { + if len(model.Windows) > 0 { + existing.Properties.UpdateConfiguration.OperatingSystem = softwareupdateconfiguration.OperatingSystemTypeWindows + v := model.Windows[0] + existing.Properties.UpdateConfiguration.Windows = &softwareupdateconfiguration.WindowsProperties{ + ExcludedKbNumbers: pointer.To(v.ExcludedKbs), + IncludedKbNumbers: pointer.To(v.IncludedKbs), + IncludedUpdateClassifications: pointer.To(softwareupdateconfiguration.WindowsUpdateClasses(strings.Join(v.Classifications, ","))), + RebootSetting: pointer.To(v.RebootSetting), + } + } else { + existing.Properties.UpdateConfiguration.Windows = &softwareupdateconfiguration.WindowsProperties{} + } + } + + if metadata.ResourceData.HasChange("duration") { + existing.Properties.UpdateConfiguration.Duration = pointer.To(model.Duration) + } + + if metadata.ResourceData.HasChange("virtual_machine_ids") { + existing.Properties.UpdateConfiguration.AzureVirtualMachines = pointer.To(model.VirtualMachines) + } + + if metadata.ResourceData.HasChange("non_azure_computer_names") { + existing.Properties.UpdateConfiguration.NonAzureComputerNames = pointer.To(model.NonAzureComputerNames) + } + + if metadata.ResourceData.HasChange("target") { + target := softwareupdateconfiguration.TargetProperties{} + if len(model.Targets) > 0 { + t := model.Targets[0] + if len(t.AzureQueries) > 0 { + aq := make([]softwareupdateconfiguration.AzureQueryProperties, 0) + for _, v := range t.AzureQueries { + q := softwareupdateconfiguration.AzureQueryProperties{} + if len(v.Locations) > 0 { + q.Locations = pointer.To(v.Locations) + } + if len(v.Scope) > 0 { + q.Scope = pointer.To(v.Scope) + } + if len(v.Tags) > 0 || v.TagFilter != "" { + q.TagSettings = &softwareupdateconfiguration.TagSettingsProperties{ + FilterOperator: pointer.To(softwareupdateconfiguration.TagOperators(v.TagFilter)), + } + tags := make(map[string][]string) + for _, tag := range v.Tags { + tags[tag.Tag] = tag.Values + } + q.TagSettings.Tags = pointer.To(tags) + } + + aq = append(aq, q) + } + + target.AzureQueries = pointer.To(aq) + } else { + target.AzureQueries = &[]softwareupdateconfiguration.AzureQueryProperties{} + } + + if len(t.NonAzureQueries) > 0 { + naqs := make([]softwareupdateconfiguration.NonAzureQueryProperties, 0) + for _, v := range t.NonAzureQueries { + naq := softwareupdateconfiguration.NonAzureQueryProperties{} + if v.FunctionAlias != "" { + naq.FunctionAlias = pointer.To(v.FunctionAlias) + } + if v.WorkspaceId != "" { + naq.WorkspaceId = pointer.To(v.WorkspaceId) + } + naqs = append(naqs, naq) + } + + target.NonAzureQueries = pointer.To(naqs) + } else { + target.NonAzureQueries = &[]softwareupdateconfiguration.NonAzureQueryProperties{} + } + + } else { + target.AzureQueries = &[]softwareupdateconfiguration.AzureQueryProperties{} + target.NonAzureQueries = &[]softwareupdateconfiguration.NonAzureQueryProperties{} + } + existing.Properties.UpdateConfiguration.Targets = pointer.To(target) + } + + if metadata.ResourceData.HasChange("schedule") { + if len(model.Schedule) == 1 { + v := model.Schedule[0] + scheduleConfig := softwareupdateconfiguration.SUCScheduleProperties{ + Description: pointer.To(v.Description), + ExpiryTime: pointer.To(v.ExpiryTime), + ExpiryTimeOffsetMinutes: pointer.To(v.ExpiryTimeOffsetMinutes), + Frequency: pointer.To(softwareupdateconfiguration.ScheduleFrequency(v.Frequency)), + Interval: pointer.To(int64(v.Interval)), + IsEnabled: pointer.To(v.IsEnabled), + NextRun: pointer.To(v.NextRun), + NextRunOffsetMinutes: pointer.To(v.NextRunOffsetMinutes), + StartTime: pointer.To(v.StartTime), + StartTimeOffsetMinutes: pointer.To(v.StartTimeOffsetMinutes), + TimeZone: pointer.To(v.TimeZone), + } + + if len(v.AdvancedWeekDays) > 0 || len(v.AdvancedMonthDays) > 0 || len(v.MonthlyOccurrence) > 0 { + advSchedule := &softwareupdateconfiguration.AdvancedSchedule{} + if len(v.AdvancedWeekDays) > 0 { + advSchedule.WeekDays = pointer.To(v.AdvancedWeekDays) + } + + if len(v.AdvancedMonthDays) > 0 { + i := make([]int64, 0) + for _, v := range v.AdvancedMonthDays { + i = append(i, int64(v)) + } + advSchedule.MonthDays = pointer.To(i) + } + + if len(v.MonthlyOccurrence) > 0 { + monthlyOccurrences := make([]softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence, 0) + for _, mo := range v.MonthlyOccurrence { + monthlyOccurrences = append(monthlyOccurrences, softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence{ + Day: pointer.To(softwareupdateconfiguration.ScheduleDay(mo.Day)), + Occurrence: pointer.To(int64(mo.Occurrence)), + }) + } + + advSchedule.MonthlyOccurrences = pointer.To(monthlyOccurrences) + } + + scheduleConfig.AdvancedSchedule = advSchedule + } + + existing.Properties.ScheduleInfo = scheduleConfig + } else { + existing.Properties.ScheduleInfo = softwareupdateconfiguration.SUCScheduleProperties{} + } + } + + if metadata.ResourceData.HasChanges("pre_task", "post_task") { + tasksConfig := &softwareupdateconfiguration.SoftwareUpdateConfigurationTasks{} + if existing.Properties.Tasks != nil { + tasksConfig = existing.Properties.Tasks + } + if len(model.PreTask) > 0 { + v := model.PreTask[0] + task := &softwareupdateconfiguration.TaskProperties{ + Parameters: pointer.To(v.Parameters), + Source: pointer.To(v.Source), + } + + tasksConfig.PreTask = task + } else { + tasksConfig.PreTask = &softwareupdateconfiguration.TaskProperties{} + } + if len(model.PostTask) > 0 { + v := model.PostTask[0] + task := &softwareupdateconfiguration.TaskProperties{ + Parameters: pointer.To(v.Parameters), + Source: pointer.To(v.Source), + } + + tasksConfig.PostTask = task + } else { + tasksConfig.PostTask = &softwareupdateconfiguration.TaskProperties{} + } + } + + if _, err = client.SoftwareUpdateConfigurationsCreate(ctx, *id, *existing, softwareupdateconfiguration.SoftwareUpdateConfigurationsCreateOperationOptions{}); err != nil { + return fmt.Errorf("creating %s: %v", id, err) + } + + return nil + }, } } @@ -1068,3 +1198,196 @@ func (m SoftwareUpdateConfigurationResource) Delete() sdk.ResourceFunc { func (m SoftwareUpdateConfigurationResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return softwareupdateconfiguration.ValidateSoftwareUpdateConfigurationID } + +func expandUpdateConfig(input SoftwareUpdateConfigurationModel) *softwareupdateconfiguration.SoftwareUpdateConfiguration { + result := &softwareupdateconfiguration.SoftwareUpdateConfiguration{ + Properties: softwareupdateconfiguration.SoftwareUpdateConfigurationProperties{ + ScheduleInfo: softwareupdateconfiguration.SUCScheduleProperties{}, + }, + } + + if len(input.Schedule) == 1 { + v := input.Schedule[0] + scheduleConfig := softwareupdateconfiguration.SUCScheduleProperties{ + Description: pointer.To(v.Description), + ExpiryTime: pointer.To(v.ExpiryTime), + ExpiryTimeOffsetMinutes: pointer.To(v.ExpiryTimeOffsetMinutes), + Frequency: pointer.To(softwareupdateconfiguration.ScheduleFrequency(v.Frequency)), + Interval: pointer.To(int64(v.Interval)), + IsEnabled: pointer.To(v.IsEnabled), + NextRun: pointer.To(v.NextRun), + NextRunOffsetMinutes: pointer.To(v.NextRunOffsetMinutes), + StartTime: pointer.To(v.StartTime), + StartTimeOffsetMinutes: pointer.To(v.StartTimeOffsetMinutes), + TimeZone: pointer.To(v.TimeZone), + } + + if len(v.AdvancedWeekDays) > 0 || len(v.AdvancedMonthDays) > 0 || len(v.MonthlyOccurrence) > 0 { + advSchedule := &softwareupdateconfiguration.AdvancedSchedule{} + if len(v.AdvancedWeekDays) > 0 { + advSchedule.WeekDays = pointer.To(v.AdvancedWeekDays) + } + + if len(v.AdvancedMonthDays) > 0 { + i := make([]int64, 0) + for _, v := range v.AdvancedMonthDays { + i = append(i, int64(v)) + } + advSchedule.MonthDays = pointer.To(i) + } + + if len(v.MonthlyOccurrence) > 0 { + monthlyOccurrences := make([]softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence, 0) + for _, mo := range v.MonthlyOccurrence { + monthlyOccurrences = append(monthlyOccurrences, softwareupdateconfiguration.AdvancedScheduleMonthlyOccurrence{ + Day: pointer.To(softwareupdateconfiguration.ScheduleDay(mo.Day)), + Occurrence: pointer.To(int64(mo.Occurrence)), + }) + } + + advSchedule.MonthlyOccurrences = pointer.To(monthlyOccurrences) + } + + scheduleConfig.AdvancedSchedule = advSchedule + } + + result.Properties.ScheduleInfo = scheduleConfig + } + + if len(input.PreTask) > 0 || len(input.PostTask) > 0 { + tasksConfig := &softwareupdateconfiguration.SoftwareUpdateConfigurationTasks{} + + if len(input.PreTask) > 0 { + v := input.PreTask[0] + task := &softwareupdateconfiguration.TaskProperties{ + Parameters: pointer.To(v.Parameters), + Source: pointer.To(v.Source), + } + + tasksConfig.PreTask = task + } + + if len(input.PostTask) > 0 { + v := input.PostTask[0] + task := &softwareupdateconfiguration.TaskProperties{ + Parameters: pointer.To(v.Parameters), + Source: pointer.To(v.Source), + } + + tasksConfig.PostTask = task + } + + result.Properties.Tasks = tasksConfig + } + + updateConfig := softwareupdateconfiguration.UpdateConfiguration{} + + if len(input.VirtualMachines) > 0 { + updateConfig.AzureVirtualMachines = pointer.To(input.VirtualMachines) + } + if input.Duration != "" { + updateConfig.Duration = pointer.To(input.Duration) + } + + if len(input.NonAzureComputerNames) > 0 { + updateConfig.NonAzureComputerNames = pointer.To(input.NonAzureComputerNames) + } + + if len(input.Targets) == 1 { + t := input.Targets[0] + target := softwareupdateconfiguration.TargetProperties{} + if len(t.AzureQueries) > 0 { + aq := make([]softwareupdateconfiguration.AzureQueryProperties, 0) + for _, v := range t.AzureQueries { + q := softwareupdateconfiguration.AzureQueryProperties{} + if len(v.Locations) > 0 { + q.Locations = pointer.To(v.Locations) + } + if len(v.Scope) > 0 { + q.Scope = pointer.To(v.Scope) + } + if len(v.Tags) > 0 || v.TagFilter != "" { + q.TagSettings = &softwareupdateconfiguration.TagSettingsProperties{ + FilterOperator: pointer.To(softwareupdateconfiguration.TagOperators(v.TagFilter)), + } + tags := make(map[string][]string) + for _, tag := range v.Tags { + tags[tag.Tag] = tag.Values + } + q.TagSettings.Tags = pointer.To(tags) + } + + aq = append(aq, q) + } + + target.AzureQueries = pointer.To(aq) + } + + if len(t.NonAzureQueries) > 0 { + naqs := make([]softwareupdateconfiguration.NonAzureQueryProperties, 0) + for _, v := range t.NonAzureQueries { + naq := softwareupdateconfiguration.NonAzureQueryProperties{} + if v.FunctionAlias != "" { + naq.FunctionAlias = pointer.To(v.FunctionAlias) + } + if v.WorkspaceId != "" { + naq.WorkspaceId = pointer.To(v.WorkspaceId) + } + naqs = append(naqs, naq) + } + + target.NonAzureQueries = pointer.To(naqs) + } + updateConfig.Targets = pointer.To(target) + } + + if len(input.Linux) == 1 { + v := input.Linux[0] + updateConfig.OperatingSystem = softwareupdateconfiguration.OperatingSystemTypeLinux + updateConfig.Linux = &softwareupdateconfiguration.LinuxProperties{ + ExcludedPackageNameMasks: pointer.To(v.ExcludedPackages), + IncludedPackageNameMasks: pointer.To(v.IncludedPackages), + } + if v.Reboot != "" { + updateConfig.Linux.RebootSetting = pointer.To(v.Reboot) + } + if !features.FourPointOhBeta() { + if v.Classification != "" { + updateConfig.Linux.IncludedPackageClassifications = pointer.To(softwareupdateconfiguration.LinuxUpdateClasses(v.Classification)) + } + } + if len(v.Classifications) > 0 { + updateConfig.Linux.IncludedPackageClassifications = pointer.To(softwareupdateconfiguration.LinuxUpdateClasses(strings.Join(v.Classifications, ","))) + } + } + + if len(input.Windows) == 1 { + v := input.Windows[0] + updateConfig.OperatingSystem = softwareupdateconfiguration.OperatingSystemTypeWindows + w := &softwareupdateconfiguration.WindowsProperties{} + if v.RebootSetting != "" { + w.RebootSetting = pointer.To(v.RebootSetting) + } + + if len(v.ExcludedKbs) > 0 { + w.ExcludedKbNumbers = pointer.To(v.ExcludedKbs) + } + + if len(v.IncludedKbs) > 0 { + w.IncludedKbNumbers = pointer.To(v.IncludedKbs) + } + + if !features.FourPointOhBeta() && len(v.Classification) == 1 { + w.IncludedUpdateClassifications = pointer.To(softwareupdateconfiguration.WindowsUpdateClasses(strings.Join(v.Classifications, ","))) + } + if len(v.Classifications) > 0 { + w.IncludedUpdateClassifications = pointer.To(softwareupdateconfiguration.WindowsUpdateClasses(strings.Join(v.Classifications, ","))) + } + + updateConfig.Windows = w + } + + result.Properties.UpdateConfiguration = updateConfig + + return result +} diff --git a/internal/services/automation/automation_software_update_configuration_resource_test.go b/internal/services/automation/automation_software_update_configuration_resource_test.go index 0811fff756e0..fb8fbc302d03 100644 --- a/internal/services/automation/automation_software_update_configuration_resource_test.go +++ b/internal/services/automation/automation_software_update_configuration_resource_test.go @@ -24,9 +24,10 @@ func newSoftwareUpdateConfigurationResource() SoftwareUpdateConfigurationResourc // The start time of the schedule must be at least 5 minutes after the time you create the schedule, // so we cannot hardcode the time string. // we use timezone as UTC so the time string should be in UTC format + ref := time.Now().Round(time.Minute) ins := SoftwareUpdateConfigurationResource{ - startTime: time.Now().Add(time.Hour * 10).In(time.UTC).Format(time.RFC3339), - expireTime: time.Now().Add(time.Hour * 50).In(time.UTC).Format(time.RFC3339), + startTime: ref.Add(time.Hour * 1).In(time.UTC).Format(time.RFC3339), + expireTime: ref.Add(time.Hour * 2).In(time.UTC).Format(time.RFC3339), } return ins } @@ -38,22 +39,99 @@ func (a SoftwareUpdateConfigurationResource) Exists(ctx context.Context, client } resp, err := client.Automation.SoftwareUpdateConfigClient.SoftwareUpdateConfigurationsGetByName(ctx, *id, softwareupdateconfiguration.SoftwareUpdateConfigurationsGetByNameOperationOptions{}) if err != nil { - return nil, fmt.Errorf("retrieving Type %s: %+v", *id, err) + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } return pointer.To(resp.Model != nil), nil } -func TestAccSoftwareUpdateConfiguration_basic(t *testing.T) { +func TestAccSoftwareUpdateConfiguration_linuxBasic(t *testing.T) { data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") r := newSoftwareUpdateConfigurationResource() data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.linuxBasic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + }) +} + +func TestAccSoftwareUpdateConfiguration_linuxComplete(t *testing.T) { + data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") + r := newSoftwareUpdateConfigurationResource() + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.linuxComplete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + }) +} + +func TestAccSoftwareUpdateConfiguration_linuxUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") + r := newSoftwareUpdateConfigurationResource() + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.linuxBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.linuxComplete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.linuxBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + }) +} + +func TestAccSoftwareUpdateConfiguration_CompleteUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") + r := newSoftwareUpdateConfigurationResource() + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.linuxBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.linuxBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), }) } @@ -68,7 +146,7 @@ func TestAccSoftwareUpdateConfiguration_withTask(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), }) } @@ -83,15 +161,15 @@ func TestAccSoftwareUpdateConfiguration_defaultTimeZone(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), { - Config: r.basic(data), + Config: r.linuxBasic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), }) } @@ -101,12 +179,12 @@ func TestAccSoftwareUpdateConfiguration_update(t *testing.T) { r := newSoftwareUpdateConfigurationResource() data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.linuxBasic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), { Config: r.update(data), @@ -114,36 +192,79 @@ func TestAccSoftwareUpdateConfiguration_update(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), }) } -func TestAccSoftwareUpdateConfiguration_windows(t *testing.T) { +func TestAccSoftwareUpdateConfiguration_windowsBasic(t *testing.T) { data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") r := newSoftwareUpdateConfigurationResource() data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.windows(data), + Config: r.windowsBasic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - // scheduleInfo.advancedSchedule always return null + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), }) } -func (a SoftwareUpdateConfigurationResource) defaultTimeZone(data acceptance.TestData) string { - return fmt.Sprintf(` +func TestAccSoftwareUpdateConfiguration_windowsComplete(t *testing.T) { + data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") + r := newSoftwareUpdateConfigurationResource() + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.windowsComplete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + }) +} +func TestAccSoftwareUpdateConfiguration_windowsUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, automation.SoftwareUpdateConfigurationResource{}.ResourceType(), "test") + r := newSoftwareUpdateConfigurationResource() + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.windowsBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.windowsComplete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + { + Config: r.windowsBasic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + // scheduleInfo.advancedSchedule always returns null - https://github.com/Azure/azure-rest-api-specs/issues/24436 + data.ImportStep("schedule.0.advanced", "schedule.0.monthly_occurrence"), + }) +} +func (a SoftwareUpdateConfigurationResource) defaultTimeZone(data acceptance.TestData) string { + return fmt.Sprintf(` %s resource "azurerm_automation_software_update_configuration" "test" { automation_account_id = azurerm_automation_account.test.id name = "acctest-suc-%[2]d" - operating_system = "Linux" linux { classification_included = "Security" @@ -186,7 +307,36 @@ resource "azurerm_automation_software_update_configuration" "test" { `, a.template(data), data.RandomInteger, a.startTime, a.expireTime) } -func (a SoftwareUpdateConfigurationResource) basic(data acceptance.TestData) string { +func (a SoftwareUpdateConfigurationResource) linuxBasic(data acceptance.TestData) string { + return fmt.Sprintf(` + +%s + +resource "azurerm_automation_software_update_configuration" "test" { + automation_account_id = azurerm_automation_account.test.id + name = "acctest-suc-%[2]d" + + linux { + classifications_included = ["Security"] + } + + target { + azure_query { + scope = [azurerm_resource_group.test.id] + locations = [azurerm_resource_group.test.location] + } + } + + schedule { + frequency = "OneTime" + } + + depends_on = [azurerm_log_analytics_linked_service.test] +} +`, a.template(data), data.RandomInteger) +} + +func (a SoftwareUpdateConfigurationResource) linuxComplete(data acceptance.TestData) string { return fmt.Sprintf(` @@ -195,13 +345,12 @@ func (a SoftwareUpdateConfigurationResource) basic(data acceptance.TestData) str resource "azurerm_automation_software_update_configuration" "test" { automation_account_id = azurerm_automation_account.test.id name = "acctest-suc-%[2]d" - operating_system = "Linux" linux { - classification_included = "Security" - excluded_packages = ["apt"] - included_packages = ["vim"] - reboot = "IfRequired" + classifications_included = ["Critical", "Security"] + excluded_packages = ["apt"] + included_packages = ["vim"] + reboot = "RebootOnly" } duration = "PT1H1M1S" @@ -222,6 +371,7 @@ resource "azurerm_automation_software_update_configuration" "test" { schedule { description = "foo-schedule" start_time = "%[3]s" + expiry_time = "%[4]s" is_enabled = true interval = 1 frequency = "Hour" @@ -241,8 +391,6 @@ resource "azurerm_automation_software_update_configuration" "test" { func (a SoftwareUpdateConfigurationResource) withTask(data acceptance.TestData) string { return fmt.Sprintf(` - - resource "azurerm_automation_runbook" "test" { name = "Get-AzureVMTutorial" location = azurerm_resource_group.test.location @@ -268,7 +416,6 @@ CONTENT resource "azurerm_automation_software_update_configuration" "test" { automation_account_id = azurerm_automation_account.test.id name = "acctest-suc-%[2]d" - operating_system = "Linux" linux { classification_included = "Security" @@ -329,7 +476,6 @@ resource "azurerm_automation_software_update_configuration" "test" { func (a SoftwareUpdateConfigurationResource) update(data acceptance.TestData) string { return fmt.Sprintf(` - %s data "azurerm_client_config" "current" {} @@ -337,13 +483,12 @@ data "azurerm_client_config" "current" {} resource "azurerm_automation_software_update_configuration" "test" { automation_account_id = azurerm_automation_account.test.id name = "acctest-suc-%[2]d" - operating_system = "Linux" linux { classification_included = "Security" excluded_packages = ["apt"] included_packages = ["vim"] - reboot = "IfRequired" + reboot = "Always" } duration = "PT2H2M2S" @@ -361,7 +506,7 @@ resource "azurerm_automation_software_update_configuration" "test" { } non_azure_query { - function_alias = "savedSearch1" + function_alias = "savedSearch2" workspace_id = azurerm_log_analytics_workspace.test.id } } @@ -371,7 +516,7 @@ resource "azurerm_automation_software_update_configuration" "test" { start_time = "%[3]s" expiry_time = "%[4]s" is_enabled = true - interval = 1 + interval = 2 frequency = "Hour" time_zone = "Etc/UTC" advanced_week_days = ["Monday", "Tuesday"] @@ -382,7 +527,35 @@ resource "azurerm_automation_software_update_configuration" "test" { `, a.template(data), data.RandomInteger, a.startTime, a.expireTime) } -func (a SoftwareUpdateConfigurationResource) windows(data acceptance.TestData) string { +func (a SoftwareUpdateConfigurationResource) windowsBasic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_automation_software_update_configuration" "test" { + automation_account_id = azurerm_automation_account.test.id + name = "acctest-suc-%[2]d" + + windows { + classifications_included = ["Security"] + } + + target { + azure_query { + scope = [azurerm_resource_group.test.id] + locations = [azurerm_resource_group.test.location] + } + } + + schedule { + frequency = "OneTime" + } + + depends_on = [azurerm_log_analytics_linked_service.test] +} +`, a.template(data), data.RandomInteger, a.startTime, a.expireTime) +} + +func (a SoftwareUpdateConfigurationResource) windowsComplete(data acceptance.TestData) string { return fmt.Sprintf(` @@ -391,11 +564,10 @@ func (a SoftwareUpdateConfigurationResource) windows(data acceptance.TestData) s resource "azurerm_automation_software_update_configuration" "test" { automation_account_id = azurerm_automation_account.test.id name = "acctest-suc-%[2]d" - operating_system = "Windows" windows { classifications_included = ["Critical", "Security"] - reboot = "IfRequired" + reboot = "RebootOnly" } duration = "PT1H1M1S" @@ -451,7 +623,7 @@ provider "azurerm" { resource "azurerm_resource_group" "test" { name = "acctestRG-auto-%[1]d" - location = "West US" + location = "%[2]s" } resource "azurerm_automation_account" "test" { @@ -474,5 +646,5 @@ resource "azurerm_log_analytics_linked_service" "test" { workspace_id = azurerm_log_analytics_workspace.test.id read_access_id = azurerm_automation_account.test.id } -`, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary) } diff --git a/website/docs/r/automation_software_update_configuration.html.markdown b/website/docs/r/automation_software_update_configuration.html.markdown index dc7f5cdcf038..7d18e4f398d6 100644 --- a/website/docs/r/automation_software_update_configuration.html.markdown +++ b/website/docs/r/automation_software_update_configuration.html.markdown @@ -80,53 +80,51 @@ The following arguments are supported: * `automation_account_id` - (Required) The ID of Automation Account to manage this Source Control. Changing this forces a new Automation Source Control to be created. -* `operating_system` - (Required) The Operating system of target machines. Possible values are `Windows` and `Linux`. - --- -* `duration` - (Optional) Maximum time allowed for the software update configuration run. using format `PT[n]H[n]M[n]S` as per ISO8601. +* `duration` - (Optional) Maximum time allowed for the software update configuration run. using format `PT[n]H[n]M[n]S` as per ISO8601. Defaults to `PT2H`. + +* `linux` - (Optional) A `linux` block as defined below. -* `linux` - (Optional) One or more `linux` blocks as defined below. +* `windows` - (Optional) A `windows` block as defined below. -* `windows` - (Optional) One or more `windows` blocks as defined below. +~> **NOTE:** One of `linux` or `windows` must be specified. -* `virtual_machine_ids` - (Optional) Specifies a list of azure resource Ids of azure virtual machines. +* `virtual_machine_ids` - (Optional) Specifies a list of Azure Resource IDs of azure virtual machines. -* `non_azure_computer_names` - (Optional) Specifies a list of names of non-azure machines for the software update configuration. +* `non_azure_computer_names` - (Optional) Specifies a list of names of non-Azure machines for the software update configuration. -* `target` - (Optional) One or more `target` blocks as defined below. +* `target` - (Optional) A `target` blocks as defined below. -* `post_task` - (Optional) One or more `post_task` blocks as defined below. +* `post_task` - (Optional) A `post_task` blocks as defined below. -* `pre_task` - (Optional) One or more `pre_task` blocks as defined below. +* `pre_task` - (Optional) A `pre_task` blocks as defined below. -* `schedule` - (Optional) One or more `schedule` blocks as defined below. +* `schedule` - (Optional) A `schedule` blocks as defined below. --- A `linux` block supports the following: -* `classification_included` - (Optional) Specifies the update classifications included in the Software Update Configuration. Possible values are `Unclassified`, `Critical`, `Security` and `Other`. +* `classifications_included` - (Optional) Specifies the list of update classifications included in the Software Update Configuration. Possible values are `Unclassified`, `Critical`, `Security` and `Other`. * `excluded_packages` - (Optional) Specifies a list of packages to excluded from the Software Update Configuration. * `included_packages` - (Optional) Specifies a list of packages to included from the Software Update Configuration. -* `reboot` - (Optional) Specifies the reboot settings after software update, possible values are `IfRequired`, `Never` and `Always` +* `reboot` - (Optional) Specifies the reboot settings after software update, possible values are `IfRequired`, `Never`, `RebootOnly` and `Always`. Defaults to `IfRequired`. --- A `windows` block supports the following: -* `classification_included` - (Optional) (Deprecated) Specifies the update classification. Possible values are `Unclassified`, `Critical`, `Security`, `UpdateRollup`, `FeaturePack`, `ServicePack`, `Definition`, `Tools` and `Updates`. - * `classifications_included` - (Optional) Specifies the list of update classification. Possible values are `Unclassified`, `Critical`, `Security`, `UpdateRollup`, `FeaturePack`, `ServicePack`, `Definition`, `Tools` and `Updates`. * `excluded_knowledge_base_numbers` - (Optional) Specifies a list of knowledge base numbers excluded. * `included_knowledge_base_numbers` - (Optional) Specifies a list of knowledge base numbers included. -* `reboot` - (Optional) Specifies the reboot settings after software update, possible values are `IfRequired`, `Never` and `Always` +* `reboot` - (Optional) Specifies the reboot settings after software update, possible values are `IfRequired`, `Never`, `RebootOnly` and `Always`. Defaults to `IfRequired`. --- @@ -184,9 +182,9 @@ A `post_task` block supports the following: A `schedule` block supports the following: -* `is_enabled` - (Optional) Whether the schedule is enabled. +* `frequency` - (Required) The frequency of the schedule. - can be either `OneTime`, `Day`, `Hour`, `Week`, or `Month`. -* `frequency` - (Optional) The frequency of the schedule. - can be either `OneTime`, `Day`, `Hour`, `Week`, or `Month`. +* `is_enabled` - (Optional) Whether the schedule is enabled. * `description` - (Optional) A description for this Schedule. @@ -198,7 +196,7 @@ A `schedule` block supports the following: * `time_zone` - (Optional) The timezone of the start time. Defaults to `Etc/UTC`. For possible values see: -* `advanced_week_days` - (Optional) List of days of the week that the job should execute on. Only valid when frequency is `Week`. +* `advanced_week_days` - (Optional) List of days of the week that the job should execute on. Only valid when frequency is `Week`. Possible values include `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday`, `Saturday`, and `Sunday`. * `advanced_month_days` - (Optional) List of days of the month that the job should execute on. Must be between `1` and `31`. `-1` for last day of the month. Only valid when frequency is `Month`.