diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index e5fb9e52dc51..7e26bc7cef5d 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -374,6 +374,13 @@ func resourceArmVirtualMachine() *schema.Resource { Optional: true, Default: false, }, + "timezone": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + ValidateFunc: validateAzureVirtualMachineTimeZone(), + }, "winrm": { Type: schema.TypeList, Optional: true, @@ -1019,6 +1026,10 @@ func flattenAzureRmVirtualMachineOsProfileWindowsConfiguration(config *compute.W result["enable_automatic_upgrades"] = *config.EnableAutomaticUpdates } + if config.TimeZone != nil { + result["timezone"] = *config.TimeZone + } + if config.WinRM != nil { listeners := make([]map[string]interface{}, 0, len(*config.WinRM.Listeners)) for _, i := range *config.WinRM.Listeners { @@ -1291,6 +1302,10 @@ func expandAzureRmVirtualMachineOsProfileWindowsConfig(d *schema.ResourceData) ( config.EnableAutomaticUpdates = &update } + if v := osProfileConfig["timezone"]; v != nil && v.(string) != "" { + config.TimeZone = utils.String(v.(string)) + } + if v := osProfileConfig["winrm"]; v != nil { winRm := v.([]interface{}) if len(winRm) > 0 { @@ -1595,6 +1610,9 @@ func resourceArmVirtualMachineStorageOsProfileWindowsConfigHash(v interface{}) i if v, ok := m["enable_automatic_upgrades"]; ok { buf.WriteString(fmt.Sprintf("%t-", v.(bool))) } + if v, ok := m["timezone"]; ok { + buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string)))) + } } return hashcode.String(buf.String()) diff --git a/azurerm/resource_arm_virtual_machine_test.go b/azurerm/resource_arm_virtual_machine_test.go index c38b656ef482..722e8be9dee2 100644 --- a/azurerm/resource_arm_virtual_machine_test.go +++ b/azurerm/resource_arm_virtual_machine_test.go @@ -3,8 +3,10 @@ package azurerm import ( "fmt" "net/http" + "testing" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2017-12-01/compute" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" ) @@ -68,3 +70,106 @@ func testCheckAzureRMVirtualMachineDestroy(s *terraform.State) error { return nil } + +func TestAccAzureRMVirtualMachine_winTimeZone(t *testing.T) { + resourceName := "azurerm_virtual_machine.test" + var vm compute.VirtualMachine + ri := acctest.RandInt() + config := testAccAzureRMVirtualMachine_winTimeZone(ri, testLocation()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineExists("azurerm_virtual_machine.test", &vm), + resource.TestCheckResourceAttr(resourceName, "os_profile_windows_config.59207889.timezone", "Pacific Standard Time"), + ), + }, + }, + }) +} + +func testAccAzureRMVirtualMachine_winTimeZone(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2012-Datacenter" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + } + + os_profile { + computer_name = "winhost01" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_windows_config { + timezone = "Pacific Standard Time" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} diff --git a/azurerm/validators.go b/azurerm/validators.go index 5b567160f0c9..3119937b6c80 100644 --- a/azurerm/validators.go +++ b/azurerm/validators.go @@ -8,6 +8,7 @@ import ( "github.com/Azure/go-autorest/autorest/date" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/satori/uuid" ) @@ -76,6 +77,120 @@ func validateIso8601Duration() schema.SchemaValidateFunc { } } +func validateAzureVirtualMachineTimeZone() schema.SchemaValidateFunc { + // Candidates are listed here: http://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/ + candidates := []string{ + "", + "Afghanistan Standard Time", + "Alaskan Standard Time", + "Arab Standard Time", + "Arabian Standard Time", + "Arabic Standard Time", + "Argentina Standard Time", + "Atlantic Standard Time", + "AUS Central Standard Time", + "AUS Eastern Standard Time", + "Azerbaijan Standard Time", + "Azores Standard Time", + "Bahia Standard Time", + "Bangladesh Standard Time", + "Belarus Standard Time", + "Canada Central Standard Time", + "Cape Verde Standard Time", + "Caucasus Standard Time", + "Cen. Australia Standard Time", + "Central America Standard Time", + "Central Asia Standard Time", + "Central Brazilian Standard Time", + "Central Europe Standard Time", + "Central European Standard Time", + "Central Pacific Standard Time", + "Central Standard Time (Mexico)", + "Central Standard Time", + "China Standard Time", + "Dateline Standard Time", + "E. Africa Standard Time", + "E. Australia Standard Time", + "E. Europe Standard Time", + "E. South America Standard Time", + "Eastern Standard Time (Mexico)", + "Eastern Standard Time", + "Egypt Standard Time", + "Ekaterinburg Standard Time", + "Fiji Standard Time", + "FLE Standard Time", + "Georgian Standard Time", + "GMT Standard Time", + "Greenland Standard Time", + "Greenwich Standard Time", + "GTB Standard Time", + "Hawaiian Standard Time", + "India Standard Time", + "Iran Standard Time", + "Israel Standard Time", + "Jordan Standard Time", + "Kaliningrad Standard Time", + "Korea Standard Time", + "Libya Standard Time", + "Line Islands Standard Time", + "Magadan Standard Time", + "Mauritius Standard Time", + "Middle East Standard Time", + "Montevideo Standard Time", + "Morocco Standard Time", + "Mountain Standard Time (Mexico)", + "Mountain Standard Time", + "Myanmar Standard Time", + "N. Central Asia Standard Time", + "Namibia Standard Time", + "Nepal Standard Time", + "New Zealand Standard Time", + "Newfoundland Standard Time", + "North Asia East Standard Time", + "North Asia Standard Time", + "Pacific SA Standard Time", + "Pacific Standard Time (Mexico)", + "Pacific Standard Time", + "Pakistan Standard Time", + "Paraguay Standard Time", + "Romance Standard Time", + "Russia Time Zone 10", + "Russia Time Zone 11", + "Russia Time Zone 3", + "Russian Standard Time", + "SA Eastern Standard Time", + "SA Pacific Standard Time", + "SA Western Standard Time", + "Samoa Standard Time", + "SE Asia Standard Time", + "Singapore Standard Time", + "South Africa Standard Time", + "Sri Lanka Standard Time", + "Syria Standard Time", + "Taipei Standard Time", + "Tasmania Standard Time", + "Tokyo Standard Time", + "Tonga Standard Time", + "Turkey Standard Time", + "Ulaanbaatar Standard Time", + "US Eastern Standard Time", + "US Mountain Standard Time", + "UTC", + "UTC+12", + "UTC-02", + "UTC-11", + "Venezuela Standard Time", + "Vladivostok Standard Time", + "W. Australia Standard Time", + "W. Central Africa Standard Time", + "W. Europe Standard Time", + "West Asia Standard Time", + "West Pacific Standard Time", + "Yakutsk Standard Time", + } + return validation.StringInSlice(candidates, true) +} + // intBetweenDivisibleBy returns a SchemaValidateFunc which tests if the provided value // is of type int and is between min and max (inclusive) and is divisible by a given number func validateIntBetweenDivisibleBy(min, max, divisor int) schema.SchemaValidateFunc { diff --git a/azurerm/validators_test.go b/azurerm/validators_test.go index a6b379ee3160..648d4770071f 100644 --- a/azurerm/validators_test.go +++ b/azurerm/validators_test.go @@ -182,3 +182,41 @@ func TestValidateIntBetweenDivisibleBy(t *testing.T) { } } } + +func TestValidateAzureVirtualMachineTimeZone(t *testing.T) { + cases := []struct { + Value string + Errors int + }{ + { + Value: "", + Errors: 0, + }, + { + Value: "UTC", + Errors: 0, + }, + { + Value: "China Standard Time", + Errors: 0, + }, + { + // Valid UTC time zone + Value: "utc-11", + Errors: 0, + }, + { + // Invalid UTC time zone + Value: "UTC-30", + Errors: 1, + }, + } + + for _, tc := range cases { + _, errors := validateAzureVirtualMachineTimeZone()(tc.Value, "unittest") + + if len(errors) != tc.Errors { + t.Fatalf("Expected validateAzureVMTimeZone to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) + } + } +} diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index 43c948d44df1..3ba5e163b4a2 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -473,6 +473,7 @@ output "principal_id" { * `provision_vm_agent` - (Optional) This value defaults to false. * `enable_automatic_upgrades` - (Optional) This value defaults to false. +* `timezone` - (Optional) Specifies the time zone of the virtual machine, [the possible values are defined here](http://jackstromberg.com/2017/01/list-of-time-zones-consumed-by-azure/). Defaults to `""`. * `winrm` - (Optional) A collection of WinRM configuration blocks as documented below. * `additional_unattend_config` - (Optional) An Additional Unattended Config block as documented below.