diff --git a/.gitignore b/.gitignore index 5f873cf88e6c..ba2d427920a0 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ website/vendor !command/test-fixtures/**/.terraform/ .env.sh + +# goenv version file +.go-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf28c5a9cab..d39825f21bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,42 @@ -## 1.15.0 (Unreleased) +## 1.16.0 (Unreleased) + +BUG FIXES: + +* `azurerm_public_ip` - correctly reading and importing the `idle_timeout_in_minutes` property [GH-1925] + +## 1.15.0 (September 14, 2018) + +FEATURES: + +* **New Resource:** `azurerm_firewall` ([#1627](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1627)) +* **New Resource:** `azurerm_firewall_network_rule_collection` ([#1627](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1627)) +* **New Resource:** `azurerm_mysql_virtual_network_rule` ([#1879](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1879)) IMPROVEMENTS: -* dependencies: upgrading to v20.1.0 of `github.com/Azure/azure-sdk-for-go` [GH-1861] -* dependencies: upgrading to v10.15.3 of `github.com/Azure/go-autorest` [GH-1861] -* sdk: upgrading to version `2018-06-01` of the Compute API's [GH-1861] -* `azurerm_automation_runbook` - support for specifying the content field [GH-1696] -* `azurerm_app_service` - adding the `virtual_network_name` property [GH-1896] -* `azurerm_app_service_slot` - adding the `virtual_network_name` property [GH-1896] -* `azurerm_sql_database` - adding the `threat_detection_policy` property [GH-1628] -* `azurerm_virtual_network` - adding validation to `name` preventing empty values [GH-1898] +* dependencies: upgrading to v20.1.0 of `github.com/Azure/azure-sdk-for-go` ([#1861](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1861)) +* dependencies: upgrading to v10.15.4 of `github.com/Azure/go-autorest` ([#1861](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1861)] [[#1909](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1909)) +* sdk: upgrading to version `2018-06-01` of the Compute API's ([#1861](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1861)) +* `azurerm_automation_runbook` - support for specifying the content field ([#1696](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1696)) +* `azurerm_app_service` - adding the `virtual_network_name` property ([#1896](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1896)) +* `azurerm_app_service_slot` - adding the `virtual_network_name` property ([#1896](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1896)) +* `azurerm_key_vault_certificate` - adding the `thumbprint` property ([#1904](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1904)) +* `azurerm_servicebus_queue` - adding validation for ISO8601 Durations ([#1921](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1921)) +* `azurerm_servicebus_topic` - adding validation for ISO8601 Durations ([#1921](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1921)) +* `azurerm_sql_database` - adding the `threat_detection_policy` property ([#1628](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1628)) +* `azurerm_virtual_network` - adding validation to `name` preventing empty values ([#1898](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1898)) +* `azurerm_virtual_machine` - support for the `managed_disk_type` of `StandardSSD_LRS` ([#1901](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1901)) +* `azurerm_virtual_machine_scale_set` - support for the `managed_disk_type` of `StandardSSD_LRS` ([#1901](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1901)) +* `azurerm_virtual_network_gateway` - additional validation ([#1899](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1899)) BUG FIXES: - -* Data Source: `azurerm_azuread_service_principal` - passing a filter containing the name to Azure rather than querying locally [GH-1862] -* Data Source: `azurerm_azuread_service_principal` - passing a filter containing the name to Azure rather than querying locally [GH-1862] -* `azurerm_role_assignment` - parsing the Resource ID during deletion [GH-1887] -* `azurerm_role_definition` - parsing the Resource ID during deletion [GH-1887] +* Data Source: `azurerm_azuread_service_principal` - passing a filter containing the name to Azure rather than querying locally ([#1862](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1862)) +* Data Source: `azurerm_azuread_service_principal` - passing a filter containing the name to Azure rather than querying locally ([#1862](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1862)) +* `azurerm_logic_app_trigger_http_request` - `relative_path` property now allows `/`s and `{}`s ([#1918](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1918)) +* `azurerm_role_assignment` - parsing the Resource ID during deletion ([#1887](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1887)) +* `azurerm_role_definition` - parsing the Resource ID during deletion ([#1887](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1887)) +* `azurerm_servicebus_namespace` - polling for the deletion of the namespace ([#1908](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1908)) ## 1.14.0 (September 06, 2018) @@ -209,4 +228,688 @@ IMPROVEMENTS: * `azurerm_servicebus_queue` - `enable_partitioning` can now be enabled for `Basic` and `Standard` tiers ([#1391](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1391)) * `azurerm_virtual_machine` - support for specifying user assigned identities ([#1448](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1448)) * `azurerm_virtual_machine` - making the `content` field in the `additional_unattend_config` block (within `os_profile_windows_config`) sensitive ([#1471](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1471)) -* `azurerm_virtual_machine_data_disk_attachment` - adding support for `write_accelerator_enabled` ([#147 +* `azurerm_virtual_machine_data_disk_attachment` - adding support for `write_accelerator_enabled` ([#1473](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1473)) +* `azurerm_virtual_machine_scale_set` - ensuring we set the `vhd_containers` field to fix a crash ([#1411](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1411)) +* `azurerm_virtual_machine_scale_set` - support for specifying user assigned identities ([#1448](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1448)) +* `azurerm_virtual_machine_scale_set` - making the `content` field in the `additional_unattend_config` block (within `os_profile_windows_config`) sensitive ([#1471](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1471)) +* `azurerm_virtual_network_gateway` - adding support for the `radius_server_address`, `radius_server_secret` and `vpn_client_protocols` fields to the Data Source ([#1505](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1505)) + +BUG FIXES: + +* `azurerm_key_vault_key` - handling the parent Key Vault being deleted ([#1535](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1535)) +* `azurerm_sql_database` - fix `requested_service_objective_name` updates ([#1503](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1503)) +* `azurerm_storage_account` - limiting the `tags` field to 128 characters to match the service ([#1524](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1524)) +* `azurerm_virtual_network_gateway` - fix `azurerm_virtual_network_gateway` crashing when `vpn_client_configuration` was not supplied ([#1505](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1505)) + +## 1.8.0 (June 28, 2018) + +FEATURES: + +* **New Resource:** `azurerm_dns_caa_record` support ([#1450](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1450)) +* **New Resource:** `azurerm_virtual_machine_data_disk_attachment` ([#1207](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1207)) + +IMPROVEMENTS: + +* dependencies: upgrading to v10.11.4 of `Azure/go-autorest` ([#1418](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1418)) +* dependencies: upgrading to v17.4.0 of `Azure/azure-sdk-for-go` ([#1418](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1418)) +* `azurerm_lb` - addtional validation on properties ([#1403](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1403)) +* `azurerm_application_gateway` - support for the `match` block for Probes ([#1446](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1446)) +* `azurerm_log_analytics_solution` - support for Sovereign Clouds ([#1410](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1410)) +* `azurerm_log_analytics_workspace` - support for Sovereign Clouds ([#1410](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1410)) +* `azurerm_log_analytics_workspace` - support for the `PerGB2018` SKU ([#1079](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1079)) +* `azurerm_mysql_server` - `GeneralPurpose` and `MemoryOptimized` sku tiers now allow 4tb for the `storage_mb` property ([#1449](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1449)) +* `azurerm_network_interface` - addtional validation on properties ([#1403](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1403)) +* `azurerm_postgresql_server` - `GeneralPurpose` and `MemoryOptimized` sku tiers now allow 4tb for the `storage_mb` property ([#1449](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1449)) +* `azurerm_postgresql_server` - adding support for version 10.0 ([#1457](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1457)) +* `azurerm_route_table` - adding the disable BGP propagation property ([#1435](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1435)) +* `azurerm_sql_database` - support for importing from a bacpac backup ([#972](https://github.com/terraform-providers/terraform-provider-azurerm/issues/972)) +* `azurerm_virtual_machine` - support for setting the TimeZone on Windows ([#1265](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1265)) + +BUG FIXES: + +* validation: ensuring IPv4/MAC addresses are detected correctly ([#1431](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1431)) + +## 1.7.0 (June 16, 2018) + +UPGRADE NOTES: + +~> **Please Note:** The field `overprovision` on the `azurerm_virtual_machine_scale_set` resource has changed from `false` to `true` to match the behaviour of Azure in this release. ([#1322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1322)) + +BUG FIXES: + +* `azurerm_key_vault` - respecting the proxy environment varibles terraform does and now can create vaults when behind a proxy ([#1393](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1393)) +* `azurerm_kubernetes_cluster` - `dns_prefix` is now required ([#1333](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1333)) +* `azurerm_network_interface` - ensuring that Public IP's/Private IP Addresses can be removed once assigned ([#1295](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1295)) +* `azurerm_public_ip` - setting the `domain_name_label` property into state ([#1287](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1287)) +* `azurerm_storage_account` - file and blob encryption is now explicity `true` by default ([#1380](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1380)) +* `azurerm_servicebus_namespace` - the `capacity` propety no longer unnecessarily forces a new resource when changed ([#1382](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1382)) +* `azurerm_virtual_machine_scale_set` - the field `overprovision` is now `true` by default ([#1322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1322)) +* `azurerm_app_service_plan` - the `name` property validation now allows understores ([#1351](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1351)) + +IMPROVEMENTS: + +* `azurerm_automation_schedule` - adding the `interval` property and supporting recurring schedules ([#1384](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1384)) +* `azurerm_dns_ns_record` - deprecated `record` properties in favor of a `records` list ([#991](https://github.com/terraform-providers/terraform-provider-azurerm/issues/991)) +* `azurerm_function_app` - adding the `identity` property ([#1369](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1369)) +* `azurerm_role_definition` - the `role_definition_id` property is now optional. The resource will now generate a random UUID if it is ommited ([#1378](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1378)) +* `azurerm_storage_account` - adding the `network_rules` property ([#1334](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1334)) +* `azurerm_storage_account` - adding the `identity` property ([#1323](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1323)) +* `azurerm_storage_blob` - adding the `content_type` property ([#1304](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1304)) +* `azurerm_virtual_machine` - support for `write_accelerator_enabled` property on Premium disks attached to MS-series machines ([#964](https://github.com/terraform-providers/terraform-provider-azurerm/issues/964)) +* `azurerm_virtual_machine_scale_set` - adding the `dns_settings` and `dns_servers` property ([#1209](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1209)) +* `azurerm_virtual_machine_scale_set` - adding the `ip_forwarding` property ([#1209](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1209)) +* `azurerm_virtual_network_gateway` - adding the properties `vpn_client_protocols`, `radius_server_address` and `radius_server_secret` ([#946](https://github.com/terraform-providers/terraform-provider-azurerm/issues/946)) +* dependencies: migrating to the un-deprecated Preview's for Container Instance, EventGrid, Log Analytics and SQL ([#1322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1322)) +* dependencies: upgrading to `2018-01-01` of the EventGrid API ([#1322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1322)) +* dependencies: upgrading to `2018-03-01` of the Monitor API ([#1322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1322)) + +## 1.6.0 (May 24, 2018) + +UPGRADE NOTES: + +~> **Please Note:** The `azurerm_mysql_server` resource has been updated from the Preview API's to the GA API's - which requires code changes in your Terraform Configuration to use the new Pricing SKU's. Upon updating to v1.6.0 - you'll need to update the configuration from the Preview SKU's to the GA SKU's. + +~> **Please Note:** The `azurerm_postgresql_server` resource has been updated from the Preview API's to the GA API's - which requires code changes in your Terraform Configuration to use the new Pricing SKU's. Upon updating to v1.6.0 - you'll need to update the configuration from the Preview SKU's to the GA SKU's. + +* `azurerm_scheduler_job_collection` - the property `max_retry_interval` on both the resource and datasource has been deprecated in favour of `max_recurrence_interval` to better match Azure ([#1218](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1218)) + +FEATURES: + +* **New Data Source:** `azurerm_storage_account_sas` ([#1011](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1011)) +* **New Resource:** `azurerm_data_lake_store` ([#1219](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1219)) +* **New Resource:** `azurerm_relay_namespace` ([#1233](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1233)) + +BUG FIXES: + +* across data-sources and resources: making Connection Strings, Keys and Passwords sensitive fields ([#1242](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1242)) +* `azurerm_virtual_machine_scale_set` - an empty `os_profile_windows_config` block no longer causes a panic ([#1224](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1224)) + +IMPROVEMENTS: + +* authorization: upgrading to API version `2018-01-01-preview` +* `azurerm_app_service` - adding support for `ip_restriction`'s ([#1231](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1231)) +* `azurerm_app_service_slot` - adding support for `ip_restriction`'s ([#1246](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1246)) +* `azurerm_container_registry` - no longer forces a new resource on SKU change ([#1264](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1264)) +* `azurerm_dns_zone` - datasource's `resource_group` field is now optional ([#1180](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1180)) +* `azurerm_mysql_database` - ignoring casing for the `charset` field ([#1281](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1281)) +* `azurerm_mysql_server` - support for the new GA Pricing SKU's ([#1154](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1154)) +* `azurerm_postgresql_database` - ignoring the casing on the `collation` field ([#1255](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1255)) +* `azurerm_postgresql_server` - support for the new GA Pricing SKU's ([#1190](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1190)) +* `azurerm_public_ip` - computed values now default to an empy string ([#1247](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1247)) +* `azurerm_role_assignment` - support for roles containing DataActions ([#1284](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1284)) +* `azurerm_servicebus_queue` - adding `dead_lettering_on_message_expiration` ([#1235](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1235)) +* `azurerm_virtual_machine_scale_set` - adding the `licence_type` property ([#1245](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1245)) +* `azurerm_virtual_machine_scale_set` - adding the `priority` property ([#1250](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1250)) + +## 1.5.0 (May 14, 2018) + +UPGRADE NOTES: + +~> **Please Note:** Prior to v1.5 Data Sources in the AzureRM Provider returned `nil` rather than an error message when a Resource didn't exist, which was a bug. In order to bring this into line with other Providers - starting in v1.5 the AzureRM Provider will return an error message when a resource doesn't exist. + +~> **Please Note:** This release fixes a bug in the `azurerm_redis_cache` resource where changes to fields weren't detected; as such you may see changes in the `redis_configuration` block, particularly with the `rdb_storage_connection_string` field. There's a bug tracking this inconsistency in [the Azure Rest API Specs Repository](https://github.com/Azure/azure-rest-api-specs/issues/3037). + +FEATURES: + +* **New Data Source:** `azurerm_cosmosdb_account` ([#1056](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1056)) +* **New Data Source:** `azurerm_kubernetes_cluster` ([#1204](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1204)) +* **New Data Source:** `azurerm_key_vault` ([#1202](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1202)) +* **New Data Source:** `azurerm_key_vault_secret` ([#1202](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1202)) +* **New Data Source:** `azurerm_route_table` ([#1203](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1203)) + +BUG FIXES: + +* `azurerm_redis_cache` - changes to the `redis_configuration` block are now detected - please see the note above for more information ([#1211](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1211)) + +IMPROVEMENTS: + +* dependencies - upgrading to v16.2.1 of `Azure/azure-sdk-for-go` ([#1198](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1198)) +* dependencies - upgrading to v10.8.1 of `Azure/go-autorest` ([#1198](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1198)) +* `azurerm_app_service` - support for HTTP2 ([#1188](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1188)) +* `azurerm_app_service` - support for Managed Service Identity ([#1130](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1130)) +* `azurerm_app_service_slot` - support for HTTP2 ([#1205](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1205)) +* `azurerm_cosmosdb_account` - added support for the `connection_strings` property ([#1194](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1194)) +* `azurerm_key_vault_certificate` - exposing the `certificate_data` ([#1200](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1200)) +* `azurerm_kubernetes_cluster` - making `kube_config_raw` a sensitive field ([#1225](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1225)) +* `azurerm_redis_cache` - Redis Caches can now be Imported ([#1211](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1211)) +* `azurerm_redis_firewall_rule` - Redis Firewall Rules can now be Imported ([#1211](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1211)) +* `azurerm_virtual_network` - guarding against nil-objects in the response ([#1208](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1208)) +* `azurerm_virtual_network_gateway` - ignoring the case of the `GatewaySubnet` ([#1141](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1141)) + +## 1.4.0 (April 26, 2018) + +UPGRADE NOTES: + +* `azurerm_cosmosdb_account` - the field `failover_policy` has been deprecated in favour of `geo_locations` to better match Azure + +FEATURES: + +* **New Data Source:** `azurerm_recovery_services_vault` ([#995](https://github.com/terraform-providers/terraform-provider-azurerm/issues/995)) +* **New Resource:** `azurerm_recovery_services_vault` ([#995](https://github.com/terraform-providers/terraform-provider-azurerm/issues/995)) +* **New Resource:** `azurerm_servicebus_subscription_rule` ([#1124](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1124)) + +IMPROVEMENTS: + +* `azurerm_app_service` - support for updating in-place ([#1125](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1125)) +* `azurerm_app_service_plan` - support for `kind` being `app` ([#1156](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1156)) +* `azurerm_cosmosdb_account` - support for `enable_automatic_failover` ([#1055](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1055)) +* `azurerm_cosmosdb_account` - support for the `ConsistentPrefix` consistncy level ([#1055](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1055)) +* `azurerm_cosmosdb_account` - `prefixes` can now be configured for locations ([#1055](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1055)) +* `azurerm_function_app` - support for updating in-place ([#1125](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1125)) +* `azurerm_key_vault` - adding cert permissions for `Purge` and `Recover` ([#1132](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1132)) +* `azurerm_key_vault` - polling to ensure the Key Vault is resolvable via DNS ([#1081](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1081)] [[#1164](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1164)) +* `azurerm_kubernetes_cluster` - only setting the Subnet ID when it's not an empty string ([#1158](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1158)) +* `azurerm_kubernetes_cluster` - exposing the clusters credentials as `kube_config` ([#953](https://github.com/terraform-providers/terraform-provider-azurerm/issues/953)) +* `azurerm_metric_alertrule` - filtering out tags prefixed with `$type` ([#1107](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1107)) +* `azurerm_virtual_machine` - loading managed disk information from Azure when the machine is stopped ([#1100](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1100)) +* `azurerm_virtual_machine` - make the `vm_size` property case insensitive ([#1131](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1131)) + +BUG FIXES: + +* `azurerm_cosmosdb_account` - locations can now be modified in-place (without requiring multiple apply's) ([#1055](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1055)) + + +## 1.3.3 (April 17, 2018) + +FEATURES: + +* **New Data Source:** `azurerm_app_service` ([#1071](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1071)) +* **New Resource:** `azurerm_app_service_custom_hostname_binding` ([#1087](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1087)) + +IMPROVEMENTS: + +* dependencies: upgrading to `v15.1.0` of `Azure/azure-sdk-for-go` ([#1099](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1099)) +* dependencies: upgrading to `v10.6.0` of `Azure/go-autorest` ([#1077](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1077)) +* `azurerm_app_service` - added support for the `https_only` field ([#1080](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1080)) +* `azurerm_app_service_slot` - added support for the `https_only` field ([#1080](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1080)) +* `azurerm_function_app` - added support for the `https_only` field ([#1080](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1080)) +* `azurerm_key_vault_certificate` - exposing the certificate's associated `secret_id` ([#1096](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1096)) +* `azurerm_redis_cache` - support for clusters on the internal network ([#1086](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1086)) +* `azurerm_servicebus_queue` - support for setting `requires_session` ([#1111](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1111)) +* `azurerm_sql_database` - changes to `collation` force a new resource ([#1066](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1066)) + +## 1.3.2 (April 04, 2018) + +FEATURES: + +* **New Resource:** `azurerm_packet_capture` ([#1044](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1044)) +* **New Resource:** `azurerm_policy_assignment` ([#1051](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1051)) + +IMPROVEMENTS: + +* `azurerm_virtual_machine_scale_set` - adds support for MSI ([#1018](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1018)) + +## 1.3.1 (March 29, 2018) + +FEATURES: + +* **New Data Source:** `azurerm_scheduler_job_collection` ([#990](https://github.com/terraform-providers/terraform-provider-azurerm/issues/990)) +* **New Data Source:** `azurerm_traffic_manager_geographical_location` ([#987](https://github.com/terraform-providers/terraform-provider-azurerm/issues/987)) +* **New Resource:** `azurerm_express_route_circuit_authorization` ([#992](https://github.com/terraform-providers/terraform-provider-azurerm/issues/992)) +* **New Resource:** `azurerm_express_route_circuit_peering` ([#1033](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1033)) +* **New Resource:** `azurerm_iothub` ([#887](https://github.com/terraform-providers/terraform-provider-azurerm/issues/887)) +* **New Resource:** `azurerm_policy_definition` ([#1010](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1010)) +* **New Resource:** `azurerm_sql_virtual_network_rule` ([#978](https://github.com/terraform-providers/terraform-provider-azurerm/issues/978)) + +IMPROVEMENTS: + +* `azurerm_app_service` - allow changing `client_affinity_enabled` without requiring a resource recreation ([#993](https://github.com/terraform-providers/terraform-provider-azurerm/issues/993)) +* `azurerm_app_service` - support for configuring `LocalSCM` source control ([#826](https://github.com/terraform-providers/terraform-provider-azurerm/issues/826)) +* `azurerm_app_service` - returning a clearer error message when the name (which needs to be globally unique) is in use ([#1037](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1037)) +* `azurerm_cosmosdb_account` - increasing the maximum value for `max_interval_in_seconds` from 100s to 86400s (1 day) ([#1000](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1000)) +* `azurerm_function_app` - returning a clearer error message when the name (which needs to be globally unique) is in use ([#1037](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1037)) +* `azurerm_network_interface` - support for attaching to Application Gateways ([#1027](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1027)) +* `azurerm_traffic_manager_endpoint` - adding support for `geo_mappings` ([#986](https://github.com/terraform-providers/terraform-provider-azurerm/issues/986)) +* `azurerm_traffic_manager_profile` - adding support for the `traffic_routing_method` `Geographic` ([#986](https://github.com/terraform-providers/terraform-provider-azurerm/issues/986)) +* `azurerm_virtual_machine_scale_sets` - support for attaching to Application Gateways ([#1027](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1027)) +* `azurerm_virtual_network_gateway` - changes to `peering_address` now force a new resource ([#1040](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1040)) + +## 1.3.0 (March 15, 2018) + +FEATURES: + +* **New Data Source:** `azurerm_cdn_profile` ([#950](https://github.com/terraform-providers/terraform-provider-azurerm/issues/950)) +* **New Data Source:** `azurerm_network_interface` ([#854](https://github.com/terraform-providers/terraform-provider-azurerm/issues/854)) +* **New Data Source:** `azurerm_public_ips` ([#304](https://github.com/terraform-providers/terraform-provider-azurerm/issues/304)) +* **New Data Source:** `azurerm_subscriptions` ([#940](https://github.com/terraform-providers/terraform-provider-azurerm/issues/940)) +* **New Resource:** `azurerm_log_analytics_solution` ([#952](https://github.com/terraform-providers/terraform-provider-azurerm/issues/952)) +* **New Resource:** `azurerm_sql_active_directory_administrator` ([#765](https://github.com/terraform-providers/terraform-provider-azurerm/issues/765)) +* **New Resource:** `azurerm_scheduler_job_collection` ([#963](https://github.com/terraform-providers/terraform-provider-azurerm/issues/963)) + +BUG FIXES: + +* `azurerm_application_gateway` - fixes a crash where `ssl_policy` isn't returned from the Azure API when importing existing resources ([#935](https://github.com/terraform-providers/terraform-provider-azurerm/issues/935)) +* `azurerm_app_service` - supporting `client_affinity_enabled` being `false` ([#973](https://github.com/terraform-providers/terraform-provider-azurerm/issues/973)) +* `azurerm_kubernetes_cluster` - exporting the FQDN ([#907](https://github.com/terraform-providers/terraform-provider-azurerm/issues/907)) +* `azurerm_sql_elasticpool` - fixing a crash where `location` isn't returned for legacy resources ([#982](https://github.com/terraform-providers/terraform-provider-azurerm/issues/982)) + +IMPROVEMENTS: + +* Data Source: `azurerm_builtin_role_definition` - loading available role definitions from Azure ([#770](https://github.com/terraform-providers/terraform-provider-azurerm/issues/770)) +* Data Source: `azurerm_managed_disk` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) +* Data Source: `azurerm_network_security_group` - support for security rules including Application Security Groups ([#925](https://github.com/terraform-providers/terraform-provider-azurerm/issues/925)) +* `azurerm_app_service_plan` - support for provisioning Consumption Plans ([#981](https://github.com/terraform-providers/terraform-provider-azurerm/issues/981)) +* `azurerm_cdn_endpoint` - adding support for GeoFilters, ProbePaths ([#967](https://github.com/terraform-providers/terraform-provider-azurerm/issues/967)) +* `azurerm_cdn_endpoint` - making the `origin` block ForceNew to match Azure ([#967](https://github.com/terraform-providers/terraform-provider-azurerm/issues/967)) +* `azurerm_function_app` - adding `client_affinity_enabled`, `use_32_bit_worker_process` and `websockets_enabled` ([#886](https://github.com/terraform-providers/terraform-provider-azurerm/issues/886)) +* `azurerm_load_balancer` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) +* `azurerm_managed_disk` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) +* `azurerm_network_interface` - setting `internal_fqdn` if it's not nil ([#977](https://github.com/terraform-providers/terraform-provider-azurerm/issues/977)) +* `azurerm_network_security_group` - support for security rules including Application Security Groups ([#925](https://github.com/terraform-providers/terraform-provider-azurerm/issues/925)) +* `azurerm_network_security_rule` - support for security rules including Application Security Groups ([#925](https://github.com/terraform-providers/terraform-provider-azurerm/issues/925)) +* `azurerm_public_ip` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) +* `azurerm_redis_cache` - add support for `notify-keyspace-events` ([#949](https://github.com/terraform-providers/terraform-provider-azurerm/issues/949)) +* `azurerm_template_deployment` - support for specifying parameters via `parameters_body` ([#404](https://github.com/terraform-providers/terraform-provider-azurerm/issues/404)) +* `azurerm_virtual_machine` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) +* `azurerm_virtual_machine_scale_set` - adding support for Availability Zones ([#811](https://github.com/terraform-providers/terraform-provider-azurerm/issues/811)) + +## 1.2.0 (March 02, 2018) + +FEATURES: + +* **New Data Source:** `azurerm_application_security_group` ([#914](https://github.com/terraform-providers/terraform-provider-azurerm/issues/914)) +* **New Resource:** `azurerm_application_security_group` ([#905](https://github.com/terraform-providers/terraform-provider-azurerm/issues/905)) +* **New Resource:** `azurerm_servicebus_topic_authorization_rule` ([#736](https://github.com/terraform-providers/terraform-provider-azurerm/issues/736)) + +BUG FIXES: + +* `azurerm_kubernetes_cluster` - an empty `linux_profile.ssh_key.keydata` no longer causes a crash ([#903](https://github.com/terraform-providers/terraform-provider-azurerm/issues/903)) +* `azurerm_kubernetes_cluster` - the `linux_profile.admin_username` and `linux_profile.ssh_key.keydata` fields now force a new resource ([#895](https://github.com/terraform-providers/terraform-provider-azurerm/issues/895)) +* `azurerm_network_interface` - the `subnet_id` field is now case insensitive ([#866](https://github.com/terraform-providers/terraform-provider-azurerm/issues/866)) +* `azurerm_network_security_group` - reverting `security_rules` to a set to fix an ordering issue ([#893](https://github.com/terraform-providers/terraform-provider-azurerm/issues/893)) +* `azurerm_virtual_machine_scale_set` - the `computer_name_prefix` field now forces a new resource ([#871](https://github.com/terraform-providers/terraform-provider-azurerm/issues/871)) + +IMPROVEMENTS: + +* authentication: adding support for Managed Service Identity ([#639](https://github.com/terraform-providers/terraform-provider-azurerm/issues/639)) +* `azurerm_container_group` - added `dns_name_label` and `FQDN` properties ([#877](https://github.com/terraform-providers/terraform-provider-azurerm/issues/877)) +* `azurerm_network_interface` - support for attaching to Application Security Groups ([#911](https://github.com/terraform-providers/terraform-provider-azurerm/issues/911)) +* `azurerm_network_security_group` - support for augmented security rules ([#781](https://github.com/terraform-providers/terraform-provider-azurerm/issues/781)) +* `azurerm_servicebus_subscription` - added support for the `forward_to` property ([#861](https://github.com/terraform-providers/terraform-provider-azurerm/issues/861)) +* `azurerm_storage_account` - adding support for `account_kind` being `StorageV2` ([#851](https://github.com/terraform-providers/terraform-provider-azurerm/issues/851)) +* `azurerm_virtual_network_gateway_connection` - support for IPsec/IKE Policies ([#834](https://github.com/terraform-providers/terraform-provider-azurerm/issues/834)) + +## 1.1.2 (February 19, 2018) + +FEATURES: + +* **New Resource:** `azurerm_kubernetes_cluster` ([#693](https://github.com/terraform-providers/terraform-provider-azurerm/issues/693)) +* **New Resource:** `azurerm_app_service_active_slot` ([#818](https://github.com/terraform-providers/terraform-provider-azurerm/issues/818)) +* **New Resource:** `azurerm_app_service_slot` ([#818](https://github.com/terraform-providers/terraform-provider-azurerm/issues/818)) + +BUG FIXES: + +* **Data Source:** `azurerm_app_service_plan`: handling a 404 not being returned as an error ([#849](https://github.com/terraform-providers/terraform-provider-azurerm/issues/849)) +* **Data Source:** `azurerm_virtual_network` - Fixing a crash when the DhcpOptions aren't specified ([#803](https://github.com/terraform-providers/terraform-provider-azurerm/issues/803)) +* `azurerm_application_gateway` - fixing crashes due to schema mismatches for existing resources ([#848](https://github.com/terraform-providers/terraform-provider-azurerm/issues/848)) +* `azurerm_storage_container` - add a retry for creation ([#846](https://github.com/terraform-providers/terraform-provider-azurerm/issues/846)) + +IMPROVEMENTS: + +* authentication: pulling the `Environment` key from the Azure CLI Config ([#842](https://github.com/terraform-providers/terraform-provider-azurerm/issues/842)) +* core: upgrading to `v12.5.0-beta` of the Azure SDK for Go ([#830](https://github.com/terraform-providers/terraform-provider-azurerm/issues/830)) +* compute: upgrading to use the `2017-12-01` API Version ([#797](https://github.com/terraform-providers/terraform-provider-azurerm/issues/797)) +* `azurerm_app_service_plan`: support for attaching to an App Service Environment ([#850](https://github.com/terraform-providers/terraform-provider-azurerm/issues/850)) +* `azurerm_container_group` - adding `restart_policy` ([#827](https://github.com/terraform-providers/terraform-provider-azurerm/issues/827)) +* `azurerm_managed_disk` - updated the validation on `disk_size_gb` / made it computed ([#800](https://github.com/terraform-providers/terraform-provider-azurerm/issues/800)) +* `azurerm_role_assignment` - add `role_definition_name` ([#775](https://github.com/terraform-providers/terraform-provider-azurerm/issues/775)) +* `azurerm_subnet` - add support for Service Endpoints ([#786](https://github.com/terraform-providers/terraform-provider-azurerm/issues/786)) +* `azurerm_virtual_machine` - changing `managed_disk_id` and `create_option` to be not ForceNew ([#813](https://github.com/terraform-providers/terraform-provider-azurerm/issues/813)) + + +## 1.1.1 (February 06, 2018) + +BUG FIXES: + +* `azurerm_public_ip` - Setting the `ip_address` field regardless of the DNS Settings ([#772](https://github.com/terraform-providers/terraform-provider-azurerm/issues/772)) +* `azurerm_virtual_machine` - ignores the case of the Managed Data Disk ID's to work around an Azure Portal bug ([#792](https://github.com/terraform-providers/terraform-provider-azurerm/issues/792)) + +FEATURES: + +* **New Data Source:** `azurerm_storage_account` ([#794](https://github.com/terraform-providers/terraform-provider-azurerm/issues/794)) +* **New Data Source:** `azurerm_virtual_network_gateway` ([#796](https://github.com/terraform-providers/terraform-provider-azurerm/issues/796)) + +## 1.1.0 (January 26, 2018) + +UPGRADE NOTES: + +* Data Source: `azurerm_builtin_role_definition` - now returns the correct UUID/GUID for the `Virtual Machines Contributor` role (previously the ID for the `Classic Virtual Machine Contributor` role was returned) ([#762](https://github.com/terraform-providers/terraform-provider-azurerm/issues/762)) +* `azurerm_snapshot` - `source_uri` now forces a new resource on changes due to behavioural changes in the Azure API ([#744](https://github.com/terraform-providers/terraform-provider-azurerm/issues/744)) + +FEATURES: + +* **New Data Source:** `azurerm_dns_zone` ([#702](https://github.com/terraform-providers/terraform-provider-azurerm/issues/702)) +* **New Resource:** `azurerm_metric_alertrule` ([#478](https://github.com/terraform-providers/terraform-provider-azurerm/issues/478)) +* **New Resource:** `azurerm_virtual_network_gateway` ([#133](https://github.com/terraform-providers/terraform-provider-azurerm/issues/133)) +* **New Resource:** `azurerm_virtual_network_gateway_connection` ([#133](https://github.com/terraform-providers/terraform-provider-azurerm/issues/133)) + +IMPROVEMENTS: + +* core: upgrading to `v12.2.0-beta` of `Azure/azure-sdk-for-go` ([#684](https://github.com/terraform-providers/terraform-provider-azurerm/issues/684)) +* core: upgrading to `v9.7.0` of `Azure/go-autorest` ([#684](https://github.com/terraform-providers/terraform-provider-azurerm/issues/684)) +* Data Source: `azurerm_builtin_role_definition` - adding extra role definitions ([#762](https://github.com/terraform-providers/terraform-provider-azurerm/issues/762)) +* `azurerm_app_service` - exposing the `outbound_ip_addresses` field ([#700](https://github.com/terraform-providers/terraform-provider-azurerm/issues/700)) +* `azurerm_function_app` - exposing the `outbound_ip_addresses` field ([#706](https://github.com/terraform-providers/terraform-provider-azurerm/issues/706)) +* `azurerm_function_app` - add support for the `always_on` and `connection_string` fields ([#695](https://github.com/terraform-providers/terraform-provider-azurerm/issues/695)) +* `azurerm_image` - add support for filtering images by a regex on the name ([#642](https://github.com/terraform-providers/terraform-provider-azurerm/issues/642)) +* `azurerm_lb` - adding support for the `Standard` SKU (in Preview) ([#665](https://github.com/terraform-providers/terraform-provider-azurerm/issues/665)) +* `azurerm_public_ip` - adding support for the `Standard` SKU (in Preview) ([#665](https://github.com/terraform-providers/terraform-provider-azurerm/issues/665)) +* `azurerm_network_security_rule` - add support for augmented security rules ([#692](https://github.com/terraform-providers/terraform-provider-azurerm/issues/692)) +* `azurerm_role_assignment` - generating a name if one isn't specified ([#685](https://github.com/terraform-providers/terraform-provider-azurerm/issues/685)) +* `azurerm_traffic_manager_profile` - adding support for setting `protocol` to `TCP` ([#742](https://github.com/terraform-providers/terraform-provider-azurerm/issues/742)) + +## 1.0.1 (January 12, 2018) + +FEATURES: + +* **New Data Source:** `azurerm_app_service_plan` ([#668](https://github.com/terraform-providers/terraform-provider-azurerm/issues/668)) +* **New Data Source:** `azurerm_eventhub_namespace` ([#673](https://github.com/terraform-providers/terraform-provider-azurerm/issues/673)) +* **New Resource:** `azurerm_function_app` ([#647](https://github.com/terraform-providers/terraform-provider-azurerm/issues/647)) + +IMPROVEMENTS: + +* core: adding a cache to the Storage Account Keys ([#634](https://github.com/terraform-providers/terraform-provider-azurerm/issues/634)) +* `azurerm_eventhub` - added support for `capture_description` ([#681](https://github.com/terraform-providers/terraform-provider-azurerm/issues/681)) +* `azurerm_eventhub_consumer_group` - adding validation for the user metadata field ([#641](https://github.com/terraform-providers/terraform-provider-azurerm/issues/641)) +* `azurerm_lb` - adding the computed field `public_ip_addresses` ([#633](https://github.com/terraform-providers/terraform-provider-azurerm/issues/633)) +* `azurerm_local_network_gateway` - add support for `tags` ([#638](https://github.com/terraform-providers/terraform-provider-azurerm/issues/638)) +* `azurerm_network_interface` - support for Accelerated Networking ([#672](https://github.com/terraform-providers/terraform-provider-azurerm/issues/672)) +* `azurerm_storage_account` - expose `primary_connection_string` and `secondary_connection_string` ([#647](https://github.com/terraform-providers/terraform-provider-azurerm/issues/647)) + + +## 1.0.0 (December 15, 2017) + +FEATURES: + +* **New Data Source:** `azurerm_network_security_group` ([#623](https://github.com/terraform-providers/terraform-provider-azurerm/issues/623)) +* **New Data Source:** `azurerm_virtual_network` ([#533](https://github.com/terraform-providers/terraform-provider-azurerm/issues/533)) +* **New Resource:** `azurerm_management_lock` ([#575](https://github.com/terraform-providers/terraform-provider-azurerm/issues/575)) +* **New Resource:** `azurerm_network_watcher` ([#571](https://github.com/terraform-providers/terraform-provider-azurerm/issues/571)) + +IMPROVEMENTS: + +* authentication - add support for the latest Azure CLI configuration ([#573](https://github.com/terraform-providers/terraform-provider-azurerm/issues/573)) +* authentication - conditional loading of the Subscription ID / Tenant ID / Environment ([#574](https://github.com/terraform-providers/terraform-provider-azurerm/issues/574)) +* core - appending additions to the User Agent, so we don't overwrite the Go SDK User Agent info ([#587](https://github.com/terraform-providers/terraform-provider-azurerm/issues/587)) +* core - Upgrading `Azure/azure-sdk-for-go` to v11.2.2-beta ([#594](https://github.com/terraform-providers/terraform-provider-azurerm/issues/594)) +* core - upgrading `Azure/go-autorest` to v9.5.2 ([#617](https://github.com/terraform-providers/terraform-provider-azurerm/issues/617)) +* core - skipping Resource Provider Registration in AutoRest when opted-out ([#630](https://github.com/terraform-providers/terraform-provider-azurerm/issues/630)) +* `azurerm_app_service` - exposing the Default Hostname as a Computed field + +## 0.3.3 (November 14, 2017) + +FEATURES: + +* **New Resource:** `azurerm_redis_firewall_rule` ([#529](https://github.com/terraform-providers/terraform-provider-azurerm/issues/529)) + +IMPROVEMENTS: + +* authentication: allow using multiple subscriptions for Azure CLI auth ([#445](https://github.com/terraform-providers/terraform-provider-azurerm/issues/445)) +* core: appending the CloudShell version to the user agent when running within CloudShell ([#483](https://github.com/terraform-providers/terraform-provider-azurerm/issues/483)) +* `azurerm_app_service` / `azurerm_app_service_plan` - adding validation for the `name` fields ([#528](https://github.com/terraform-providers/terraform-provider-azurerm/issues/528)) +* `azurerm_container_registry` - Migration: Fixing a crash when the storage_account block is nil ([#551](https://github.com/terraform-providers/terraform-provider-azurerm/issues/551)) +* `azurerm_lb_nat_rule`: support for floating IP's ([#542](https://github.com/terraform-providers/terraform-provider-azurerm/issues/542)) +* `azurerm_public_ip` - Clarify the error message for the validation of domain name label ([#485](https://github.com/terraform-providers/terraform-provider-azurerm/issues/485)) +* `azurerm_network_security_group` - fixing a crash when changes were made outside of Terraform ([#492](https://github.com/terraform-providers/terraform-provider-azurerm/issues/492)) +* `azurerm_redis_cache`: support for Patch Schedules ([#540](https://github.com/terraform-providers/terraform-provider-azurerm/issues/540)) +* `azurerm_virtual_machine` - ensuring `vhd_uri` is validated ([#470](https://github.com/terraform-providers/terraform-provider-azurerm/issues/470)) +* `azurerm_virtual_machine_scale_set`: fixing a crash where accelerated networking isn't returned by the API ([#480](https://github.com/terraform-providers/terraform-provider-azurerm/issues/480)) + +## 0.3.2 (October 30, 2017) + +FEATURES: + +* **New Resource:** `azurerm_application_gateway` ([#413](https://github.com/terraform-providers/terraform-provider-azurerm/issues/413)) + +IMPROVEMENTS: + + - `azurerm_virtual_machine_scale_set` - Add nil check to os disk ([#436](https://github.com/terraform-providers/terraform-provider-azurerm/issues/436)) + + - `azurerm_key_vault` - Increased timeout on dns availability ([#457](https://github.com/terraform-providers/terraform-provider-azurerm/issues/457)) + + - `azurerm_route_table` - Fix issue when routes are computed ([#450](https://github.com/terraform-providers/terraform-provider-azurerm/issues/450)) + +## 0.3.1 (October 21, 2017) + +IMPROVEMENTS: + + - `azurerm_virtual_machine_scale_set` - Updating this resource with the v11 of the Azure SDK for Go ([#448](https://github.com/terraform-providers/terraform-provider-azurerm/issues/448)) + +## 0.3.0 (October 17, 2017) + +UPGRADE NOTES: + + - `azurerm_automation_account` - the SKU `Free` has been replaced with `Basic`. + - `azurerm_container_registry` - Azure has updated the SKU from `Basic` to `Classic`, with new `Basic`, `Standard` and `Premium` SKU's introduced. + - `azurerm_container_registry` - the `storage_account` block is now `storage_account_id` and is only required for `Classic` SKU's + - `azurerm_key_vault` - `certificate_permissions`, `key_permissions` and `secret_permissions` have all had the `All` option removed by Azure. Each permission now needs to be specified manually. + * `azurerm_route_table` - `route` is no longer computed + - `azurerm_servicebus_namespace` - The `capacity` field can only be set for `Premium` SKU's + - `azurerm_servicebus_queue` - The `enable_batched_operations` and `support_ordering` fields have been deprecated by Azure. + - `azurerm_servicebus_subscription` - The `dead_lettering_on_filter_evaluation_exceptions` has been removed by Azure. + - `azurerm_servicebus_topic` - The `enable_filtering_messages_before_publishing` field has been removed by Azure. + +FEATURES: + +* **New Data Source:** `azurerm_builtin_role_definition` ([#384](https://github.com/terraform-providers/terraform-provider-azurerm/issues/384)) +* **New Data Source:** `azurerm_image` ([#382](https://github.com/terraform-providers/terraform-provider-azurerm/issues/382)) +* **New Data Source:** `azurerm_key_vault_access_policy` ([#423](https://github.com/terraform-providers/terraform-provider-azurerm/issues/423)) +* **New Data Source:** `azurerm_platform_image` ([#375](https://github.com/terraform-providers/terraform-provider-azurerm/issues/375)) +* **New Data Source:** `azurerm_role_definition` ([#414](https://github.com/terraform-providers/terraform-provider-azurerm/issues/414)) +* **New Data Source:** `azurerm_snapshot` ([#420](https://github.com/terraform-providers/terraform-provider-azurerm/issues/420)) +* **New Data Source:** `azurerm_subnet` ([#411](https://github.com/terraform-providers/terraform-provider-azurerm/issues/411)) +* **New Resource:** `azurerm_key_vault_certificate` ([#408](https://github.com/terraform-providers/terraform-provider-azurerm/issues/408)) +* **New Resource:** `azurerm_role_assignment` ([#414](https://github.com/terraform-providers/terraform-provider-azurerm/issues/414)) +* **New Resource:** `azurerm_role_definition` ([#414](https://github.com/terraform-providers/terraform-provider-azurerm/issues/414)) +* **New Resource:** `azurerm_snapshot` ([#420](https://github.com/terraform-providers/terraform-provider-azurerm/issues/420)) + +IMPROVEMENTS: + +* Upgrading to v11 of the Azure SDK for Go ([#367](https://github.com/terraform-providers/terraform-provider-azurerm/issues/367)) +* `azurerm_client_config` - updating the data source to work when using AzureCLI auth ([#393](https://github.com/terraform-providers/terraform-provider-azurerm/issues/393)) +* `azurerm_container_group` - add support for volume mounts ([#366](https://github.com/terraform-providers/terraform-provider-azurerm/issues/366)) +* `azurerm_key_vault` - fix a crash when no certificate_permissions are defined ([#374](https://github.com/terraform-providers/terraform-provider-azurerm/issues/374)) +* `azurerm_key_vault` - waiting for the DNS to propagate ([#401](https://github.com/terraform-providers/terraform-provider-azurerm/issues/401)) +* `azurerm_managed_disk` - support for creating Managed Disks from Platform Images by supporting "FromImage" ([#399](https://github.com/terraform-providers/terraform-provider-azurerm/issues/399)) +* `azurerm_managed_disk` - support for creating Encrypted Managed Disks ([#399](https://github.com/terraform-providers/terraform-provider-azurerm/issues/399)) +* `azurerm_mysql_*` - Ensuring we register the MySQL Resource Provider ([#397](https://github.com/terraform-providers/terraform-provider-azurerm/issues/397)) +* `azurerm_network_interface` - exposing all of the Private IP Addresses assigned to the NIC ([#409](https://github.com/terraform-providers/terraform-provider-azurerm/issues/409)) +* `azurerm_network_security_group` / `azurerm_network_security_rule` - refactoring ([#405](https://github.com/terraform-providers/terraform-provider-azurerm/issues/405)) +* `azurerm_route_table` - removing routes when none are specified ([#403](https://github.com/terraform-providers/terraform-provider-azurerm/issues/403)) +* `azurerm_route_table` - refactoring `route` from a Set to a List ([#402](https://github.com/terraform-providers/terraform-provider-azurerm/issues/402)) +* `azurerm_route` - refactoring `route` from a Set to a List ([#402](https://github.com/terraform-providers/terraform-provider-azurerm/issues/402)) +* `azurerm_storage_account` - support for File Encryption ([#363](https://github.com/terraform-providers/terraform-provider-azurerm/issues/363)) +* `azurerm_storage_account` - support for Custom Domain ([#363](https://github.com/terraform-providers/terraform-provider-azurerm/issues/363)) +* `azurerm_storage_account` - splitting the storage account Tier and Replication out into separate fields ([#363](https://github.com/terraform-providers/terraform-provider-azurerm/issues/363)) +- `azurerm_storage_account` - returning a user friendly error when trying to provision a Blob Storage Account with ZRS redundancy ([#421](https://github.com/terraform-providers/terraform-provider-azurerm/issues/421)) +* `azurerm_subnet` - making it possible to remove Network Security Groups / Route Tables ([#411](https://github.com/terraform-providers/terraform-provider-azurerm/issues/411)) +* `azurerm_virtual_machine` - fixing a bug where `additional_unattend_config.content` was being updated unintentionally ([#377](https://github.com/terraform-providers/terraform-provider-azurerm/issues/377)) +* `azurerm_virtual_machine` - switching to use Lists instead of Sets ([#426](https://github.com/terraform-providers/terraform-provider-azurerm/issues/426)) +* `azurerm_virtual_machine_scale_set` - fixing a bug where `additional_unattend_config.content` was being updated unintentionally ([#377](https://github.com/terraform-providers/terraform-provider-azurerm/issues/377)) +* `azurerm_virtual_machine_scale_set` - support for multiple network profiles ([#378](https://github.com/terraform-providers/terraform-provider-azurerm/issues/378)) + +## 0.2.2 (September 28, 2017) + +FEATURES: + +* **New Resource:** `azurerm_key_vault_key` ([#356](https://github.com/terraform-providers/terraform-provider-azurerm/issues/356)) +* **New Resource:** `azurerm_log_analytics_workspace` ([#331](https://github.com/terraform-providers/terraform-provider-azurerm/issues/331)) +* **New Resource:** `azurerm_mysql_configuration` ([#352](https://github.com/terraform-providers/terraform-provider-azurerm/issues/352)) +* **New Resource:** `azurerm_mysql_database` ([#352](https://github.com/terraform-providers/terraform-provider-azurerm/issues/352)) +* **New Resource:** `azurerm_mysql_firewall_rule` ([#352](https://github.com/terraform-providers/terraform-provider-azurerm/issues/352)) +* **New Resource:** `azurerm_mysql_server` ([#352](https://github.com/terraform-providers/terraform-provider-azurerm/issues/352)) + +IMPROVEMENTS: + +* Updating the provider initialization & adding a `skip_credentials_validation` field to the provider for some advanced scenarios ([#322](https://github.com/terraform-providers/terraform-provider-azurerm/issues/322)) + +## 0.2.1 (September 25, 2017) + +FEATURES: + +* **New Resource:** `azurerm_automation_account` ([#257](https://github.com/terraform-providers/terraform-provider-azurerm/issues/257)) +* **New Resource:** `azurerm_automation_credential` ([#257](https://github.com/terraform-providers/terraform-provider-azurerm/issues/257)) +* **New Resource:** `azurerm_automation_runbook` ([#257](https://github.com/terraform-providers/terraform-provider-azurerm/issues/257)) +* **New Resource:** `azurerm_automation_schedule` ([#257](https://github.com/terraform-providers/terraform-provider-azurerm/issues/257)) +* **New Resource:** `azurerm_app_service` ([#344](https://github.com/terraform-providers/terraform-provider-azurerm/issues/344)) + +IMPROVEMENTS: + +* `azurerm_client_config` - adding `service_principal_application_id` ([#348](https://github.com/terraform-providers/terraform-provider-azurerm/issues/348)) +* `azurerm_key_vault` - adding `application_id` and `certificate_permissions` ([#348](https://github.com/terraform-providers/terraform-provider-azurerm/issues/348)) + +BUG FIXES: + +* `azurerm_virtual_machine_scale_set` - fix panic with `additional_unattend_config` block ([#266](https://github.com/terraform-providers/terraform-provider-azurerm/issues/266)) + +## 0.2.0 (September 15, 2017) + +FEATURES: + +* **Support for authenticating using the Azure CLI** ([#316](https://github.com/terraform-providers/terraform-provider-azurerm/issues/316)) +* **New Resource:** `azurerm_container_group` ([#333](https://github.com/terraform-providers/terraform-provider-azurerm/issues/333)] [[#311](https://github.com/terraform-providers/terraform-provider-azurerm/issues/311)] [[#338](https://github.com/terraform-providers/terraform-provider-azurerm/issues/338)) + +IMPROVEMENTS: + +* `azurerm_app_service_plan` - support for Linux App Service Plans ([#332](https://github.com/terraform-providers/terraform-provider-azurerm/issues/332)) +* `azurerm_postgresql_server` - supporting additional storage sizes ([#239](https://github.com/terraform-providers/terraform-provider-azurerm/issues/239)) +* `azurerm_public_ip` - verifying the ID is valid before importing ([#320](https://github.com/terraform-providers/terraform-provider-azurerm/issues/320)) +* `azurerm_sql_server` - verifying the name is valid before creating ([#323](https://github.com/terraform-providers/terraform-provider-azurerm/issues/323)) +* `resource_group_name` - validation has been added to all resources that use this attribute ([#330](https://github.com/terraform-providers/terraform-provider-azurerm/issues/330)) + +## 0.1.7 (September 11, 2017) + +FEATURES: + +* **New Resource:** `azurerm_postgresql_configuration` ([#210](https://github.com/terraform-providers/terraform-provider-azurerm/issues/210)) +* **New Resource:** `azurerm_postgresql_database` ([#210](https://github.com/terraform-providers/terraform-provider-azurerm/issues/210)) +* **New Resource:** `azurerm_postgresql_firewall_rule` ([#210](https://github.com/terraform-providers/terraform-provider-azurerm/issues/210)) +* **New Resource:** `azurerm_postgresql_server` ([#210](https://github.com/terraform-providers/terraform-provider-azurerm/issues/210)) + +IMPROVEMENTS: + +* `azurerm_cdn_endpoint` - defaulting the `http_port` and `https_port` ([#301](https://github.com/terraform-providers/terraform-provider-azurerm/issues/301)) +* `azurerm_cosmos_db_account`: allow setting the Kind to MongoDB/GlobalDocumentDB ([#299](https://github.com/terraform-providers/terraform-provider-azurerm/issues/299)) + +## 0.1.6 (August 31, 2017) + +FEATURES: + +* **New Data Source**: `azurerm_subscription` ([#285](https://github.com/terraform-providers/terraform-provider-azurerm/issues/285)) +* **New Resource:** `azurerm_app_service_plan` ([#1](https://github.com/terraform-providers/terraform-provider-azurerm/issues/1)) +* **New Resource:** `azurerm_eventgrid_topic` ([#260](https://github.com/terraform-providers/terraform-provider-azurerm/issues/260)) +* **New Resource:** `azurerm_key_vault_secret` ([#269](https://github.com/terraform-providers/terraform-provider-azurerm/issues/269)) + +IMPROVEMENTS: + +* `azurerm_image` - added a default to the `caching` field ([#259](https://github.com/terraform-providers/terraform-provider-azurerm/issues/259)) +* `azurerm_key_vault` - validation for the `name` field ([#270](https://github.com/terraform-providers/terraform-provider-azurerm/issues/270)) +* `azurerm_network_interface` - support for multiple IP Configurations / setting the Primary IP Configuration ([#245](https://github.com/terraform-providers/terraform-provider-azurerm/issues/245)) +* `azurerm_resource_group` - poll until the resource group is created (by migrating to the Azure SDK for Go) ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)) +* `azurerm_search_service` - migrating to use the Azure SDK for Go ([#283](https://github.com/terraform-providers/terraform-provider-azurerm/issues/283)) +* `azurerm_sql_*` - ensuring deleted resources are detected ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)] / [[#255](https://github.com/terraform-providers/terraform-provider-azurerm/issues/255)) +* `azurerm_sql_database` - Import Support ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)) +* `azurerm_sql_database` - migrating to using the Azure SDK for Go ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)) +* `azurerm_sql_firewall_rule` - migrating to using the Azure SDK for Go ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)) +* `azurerm_sql_server` - added checks to handle `name` not being globally unique ([#189](https://github.com/terraform-providers/terraform-provider-azurerm/issues/189)) +* `azurerm_sql_server` - making `administrator_login` `ForceNew` ([#189](https://github.com/terraform-providers/terraform-provider-azurerm/issues/189)) +* `azurerm_sql_server` - migrate to using the azure-sdk-for-go ([#189](https://github.com/terraform-providers/terraform-provider-azurerm/issues/189)) +* `azurerm_virtual_machine` - Force recreation if `storage_data_disk`.`create_option` changes ([#240](https://github.com/terraform-providers/terraform-provider-azurerm/issues/240)) +* `azurerm_virtual_machine_scale_set` - Fix address issue when setting the `winrm` block ([#271](https://github.com/terraform-providers/terraform-provider-azurerm/issues/271)) +* updating to `v10.3.0-beta` of the Azure SDK for Go ([#258](https://github.com/terraform-providers/terraform-provider-azurerm/issues/258)) +* Removing the (now unused) Riviera SDK ([#289](https://github.com/terraform-providers/terraform-provider-azurerm/issues/289)] [[#291](https://github.com/terraform-providers/terraform-provider-azurerm/issues/291)) + +BUG FIXES: + +* `azurerm_cosmosdb_account` - fixing the validation on the name field ([#263](https://github.com/terraform-providers/terraform-provider-azurerm/issues/263)) +* `azurerm_sql_server` - handle deleted servers correctly ([#189](https://github.com/terraform-providers/terraform-provider-azurerm/issues/189)) +* Fixing the `Microsoft.Insights` Resource Provider Registration ([#282](https://github.com/terraform-providers/terraform-provider-azurerm/issues/282)) + +## 0.1.5 (August 09, 2017) + +IMPROVEMENTS: + +* `azurerm_sql_*` - upgrading to version `2014-04-01` of the SQL API's ([#201](https://github.com/terraform-providers/terraform-provider-azurerm/issues/201)) +* `azurerm_virtual_machine` - support for the `Windows_Client` Hybrid Use Benefit type ([#212](https://github.com/terraform-providers/terraform-provider-azurerm/issues/212)) +* `azurerm_virtual_machine_scale_set` - support for custom images and managed disks ([#203](https://github.com/terraform-providers/terraform-provider-azurerm/issues/203)) + +BUG FIXES: + +* `azurerm_sql_database` - fixing creating a DB with a PointInTimeRestore ([#197](https://github.com/terraform-providers/terraform-provider-azurerm/issues/197)) +* `azurerm_virtual_machine` - fix a crash when the properties for a network inteface aren't returned ([#208](https://github.com/terraform-providers/terraform-provider-azurerm/issues/208)) +* `azurerm_virtual_machine` - changes to custom data should force new resource ([#211](https://github.com/terraform-providers/terraform-provider-azurerm/issues/211)) +* `azurerm_virtual_machine` - fixes a crash caused by an empty `os_profile_windows_config` block ([#222](https://github.com/terraform-providers/terraform-provider-azurerm/issues/222)) +* Checking to ensure the HTTP Response isn't `nil` before accessing it (fixes ([#200](https://github.com/terraform-providers/terraform-provider-azurerm/issues/200)]) [[#204](https://github.com/terraform-providers/terraform-provider-azurerm/issues/204)) + +## 0.1.4 (July 26, 2017) + +BUG FIXES: + +* `azurerm_dns_*` - upgrading to version `2016-04-01` of the Azure DNS API by switching from Riviera -> Azure SDK for Go ([#192](https://github.com/terraform-providers/terraform-provider-azurerm/issues/192)) + +## 0.1.3 (July 21, 2017) + +FEATURES: + +* **New Resource:** `azurerm_dns_ptr_record` ([#141](https://github.com/terraform-providers/terraform-provider-azurerm/issues/141)) +* **New Resource:**`azurerm_image` ([#8](https://github.com/terraform-providers/terraform-provider-azurerm/issues/8)) +* **New Resource:** `azurerm_servicebus_queue` ([#151](https://github.com/terraform-providers/terraform-provider-azurerm/issues/151)) + +IMPROVEMENTS: + +* `azurerm_client_config` - added a `service_principal_object_id` attribute to the data source ([#175](https://github.com/terraform-providers/terraform-provider-azurerm/issues/175)) +* `azurerm_search_service` - added import support ([#172](https://github.com/terraform-providers/terraform-provider-azurerm/issues/172)) +* `azurerm_servicebus_topic` - added a `status` field to allow disabling the topic ([#150](https://github.com/terraform-providers/terraform-provider-azurerm/issues/150)) +* `azurerm_storage_account` - Added support for Require secure transfer ([#167](https://github.com/terraform-providers/terraform-provider-azurerm/issues/167)) +* `azurerm_storage_table` - updating the name validation ([#143](https://github.com/terraform-providers/terraform-provider-azurerm/issues/143)) +* `azurerm_virtual_machine` - making `admin_password` optional for Linux VM's ([#154](https://github.com/terraform-providers/terraform-provider-azurerm/issues/154)) +* `azurerm_virtual_machine_scale_set` - adding a `plan` block for Marketplace images ([#161](https://github.com/terraform-providers/terraform-provider-azurerm/issues/161)) + +## 0.1.2 (June 29, 2017) + +FEATURES: + +* **New Data Source:** `azurerm_managed_disk` ([#121](https://github.com/terraform-providers/terraform-provider-azurerm/issues/121)) +* **New Resource:** `azurerm_application_insights` ([#3](https://github.com/terraform-providers/terraform-provider-azurerm/issues/3)) +* **New Resource:** `azurerm_cosmosdb_account` ([#108](https://github.com/terraform-providers/terraform-provider-azurerm/issues/108)) +* `azurerm_network_interface` now supports import ([#119](https://github.com/terraform-providers/terraform-provider-azurerm/issues/119)) + +IMPROVEMENTS: + +* Ensuring consistency in when storing the `location` field in the state for the `azurerm_availability_set`, `azurerm_express_route_circuit`, `azurerm_load_balancer`, `azurerm_local_network_gateway`, `azurerm_managed_disk`, `azurerm_network_security_group` +`azurerm_public_ip`, `azurerm_resource_group`, `azurerm_route_table`, `azurerm_storage_account`, `azurerm_virtual_machine` and `azurerm_virtual_network` resources ([#123](https://github.com/terraform-providers/terraform-provider-azurerm/issues/123)) +* `azurerm_redis_cache` - now supports backup settings for Premium Redis Cache's ([#130](https://github.com/terraform-providers/terraform-provider-azurerm/issues/130)) +* `azurerm_storage_account` - exposing a formatted Connection String for Blob access ([#142](https://github.com/terraform-providers/terraform-provider-azurerm/issues/142)) + +BUG FIXES: + +* `azurerm_cdn_endpoint` - fixing update of the `origin_host_header` ([#134](https://github.com/terraform-providers/terraform-provider-azurerm/issues/134)) +* `azurerm_container_service` - exposes the FQDN of the `master_profile` as a computed field ([#125](https://github.com/terraform-providers/terraform-provider-azurerm/issues/125)) +* `azurerm_key_vault` - fixing import / the validation on Access Policies ([#124](https://github.com/terraform-providers/terraform-provider-azurerm/issues/124)) +* `azurerm_network_interface` - Normalizing the location field in the state ([#122](https://github.com/terraform-providers/terraform-provider-azurerm/issues/122)) +* `azurerm_network_interface` - fixing a crash when importing a NIC with a Public IP ([#128](https://github.com/terraform-providers/terraform-provider-azurerm/issues/128)) +* `azurerm_network_security_rule`: `network_security_group_name` is now `ForceNew` ([#138](https://github.com/terraform-providers/terraform-provider-azurerm/issues/138)) +* `azurerm_subnet` now correctly detects changes to Network Securtiy Groups and Routing Table's ([#113](https://github.com/terraform-providers/terraform-provider-azurerm/issues/113)) +* `azurerm_virtual_machine_scale_set` - making `storage_profile_os_disk`.`name` optional ([#129](https://github.com/terraform-providers/terraform-provider-azurerm/issues/129)) + +## 0.1.1 (June 21, 2017) + +BUG FIXES: + +* Sort ResourceID.Path keys for consistent output ([#116](https://github.com/terraform-providers/terraform-provider-azurerm/issues/116)) + +## 0.1.0 (June 20, 2017) + +BACKWARDS INCOMPATIBILITIES / NOTES: + +FEATURES: + +* **New Data Source:** `azurerm_resource_group` [[#15022](https://github.com/terraform-providers/terraform-provider-azurerm/issues/15022)](https://github.com/hashicorp/terraform/pull/15022) + +IMPROVEMENTS: + +* Add diff supress func to endpoint_location [[#15094](https://github.com/terraform-providers/terraform-provider-azurerm/issues/15094)](https://github.com/hashicorp/terraform/pull/15094) + +BUG FIXES: + +* Fixing the Deadlock issue ([#6](https://github.com/terraform-providers/terraform-provider-azurerm/issues/6)) diff --git a/azurerm/config.go b/azurerm/config.go index f3d36f56992e..3b107b149f70 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -141,6 +141,7 @@ type ArmClient struct { mysqlDatabasesClient mysql.DatabasesClient mysqlFirewallRulesClient mysql.FirewallRulesClient mysqlServersClient mysql.ServersClient + mysqlVirtualNetworkRulesClient mysql.VirtualNetworkRulesClient postgresqlConfigurationsClient postgresql.ConfigurationsClient postgresqlDatabasesClient postgresql.DatabasesClient postgresqlFirewallRulesClient postgresql.FirewallRulesClient @@ -184,6 +185,7 @@ type ArmClient struct { // Networking applicationGatewayClient network.ApplicationGatewaysClient applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient + azureFirewallsClient network.AzureFirewallsClient expressRouteAuthsClient network.ExpressRouteCircuitAuthorizationsClient expressRouteCircuitClient network.ExpressRouteCircuitsClient expressRoutePeeringsClient network.ExpressRouteCircuitPeeringsClient @@ -602,6 +604,10 @@ func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth auto c.configureClient(&mysqlServersClient.Client, auth) c.mysqlServersClient = mysqlServersClient + mysqlVirtualNetworkRulesClient := mysql.NewVirtualNetworkRulesClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&mysqlVirtualNetworkRulesClient.Client, auth) + c.mysqlVirtualNetworkRulesClient = mysqlVirtualNetworkRulesClient + // PostgreSQL postgresqlConfigClient := postgresql.NewConfigurationsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&postgresqlConfigClient.Client, auth) @@ -753,6 +759,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a c.configureClient(&appSecurityGroupsClient.Client, auth) c.applicationSecurityGroupsClient = appSecurityGroupsClient + azureFirewallsClient := network.NewAzureFirewallsClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&azureFirewallsClient.Client, auth) + c.azureFirewallsClient = azureFirewallsClient + expressRouteAuthsClient := network.NewExpressRouteCircuitAuthorizationsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&expressRouteAuthsClient.Client, auth) c.expressRouteAuthsClient = expressRouteAuthsClient diff --git a/azurerm/data_source_builtin_role_definition_test.go b/azurerm/data_source_builtin_role_definition_test.go index 868fe1e53494..e12f59d71af5 100644 --- a/azurerm/data_source_builtin_role_definition_test.go +++ b/azurerm/data_source_builtin_role_definition_test.go @@ -91,7 +91,7 @@ func TestAccDataSourceAzureRMBuiltInRoleDefinition_virtualMachineContributor(t * resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(dataSourceName, "permissions.0.actions.#", "37"), + resource.TestCheckResourceAttr(dataSourceName, "permissions.0.actions.#", "38"), resource.TestCheckResourceAttr(dataSourceName, "permissions.0.actions.0", "Microsoft.Authorization/*/read"), resource.TestCheckResourceAttr(dataSourceName, "permissions.0.actions.15", "Microsoft.Network/networkSecurityGroups/join/action"), resource.TestCheckResourceAttr(dataSourceName, "permissions.0.not_actions.#", "0"), diff --git a/azurerm/helpers/azure/firewall.go b/azurerm/helpers/azure/firewall.go new file mode 100644 index 000000000000..b161e5e7a2ae --- /dev/null +++ b/azurerm/helpers/azure/firewall.go @@ -0,0 +1,42 @@ +package azure + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-04-01/network" +) + +// The API requires InternalPublicIPAddress to be set when for a CreateOrUpdate +// operation, but Get operations return the property as PublicIPAddress +// so we need to go through and copy the value to the correct property. +func FirewallFixIPConfiguration(input *[]network.AzureFirewallIPConfiguration) (*[]network.AzureFirewallIPConfiguration, error) { + if input == nil { + return nil, fmt.Errorf("`input` was nil") + } + + results := make([]network.AzureFirewallIPConfiguration, 0) + for _, config := range *input { + if config.Subnet == nil || config.Subnet.ID == nil { + return nil, fmt.Errorf("`config.Subnet.ID` was nil") + } + + if config.PublicIPAddress == nil || config.PublicIPAddress.ID == nil { + return nil, fmt.Errorf("`config.PublicIPAddress.ID` was nil") + } + + result := network.AzureFirewallIPConfiguration{ + Name: config.Name, + AzureFirewallIPConfigurationPropertiesFormat: &network.AzureFirewallIPConfigurationPropertiesFormat{ + Subnet: &network.SubResource{ + ID: config.Subnet.ID, + }, + InternalPublicIPAddress: &network.SubResource{ + ID: config.PublicIPAddress.ID, + }, + }, + } + results = append(results, result) + } + + return &results, nil +} diff --git a/azurerm/import_arm_public_ip_test.go b/azurerm/import_arm_public_ip_test.go deleted file mode 100644 index 629bd531e2a3..000000000000 --- a/azurerm/import_arm_public_ip_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package azurerm - -import ( - "fmt" - "os" - "regexp" - "testing" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" -) - -func TestAccAzureRMPublicIpStatic_importBasic(t *testing.T) { - resourceName := "azurerm_public_ip.test" - - ri := acctest.RandInt() - config := testAccAzureRMPublicIPStatic_basic(ri, testLocation()) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMPublicIpDestroy, - Steps: []resource.TestStep{ - { - Config: config, - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAzureRMPublicIpStatic_importBasic_withZone(t *testing.T) { - resourceName := "azurerm_public_ip.test" - - ri := acctest.RandInt() - config := testAccAzureRMPublicIPStatic_basic_withZone(ri, testLocation()) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMPublicIpDestroy, - Steps: []resource.TestStep{ - { - Config: config, - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAzureRMPublicIpStatic_importBasic_withDNSLabel(t *testing.T) { - resourceName := "azurerm_public_ip.test" - - ri := acctest.RandInt() - dnl := fmt.Sprintf("acctestdnl-%d", ri) - config := testAccAzureRMPublicIPStatic_basic_withDNSLabel(ri, testLocation(), dnl) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMPublicIpDestroy, - Steps: []resource.TestStep{ - { - Config: config, - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAzureRMPublicIpStatic_importIdError(t *testing.T) { - resourceName := "azurerm_public_ip.test" - - ri := acctest.RandInt() - config := testAccAzureRMPublicIPStatic_basic(ri, testLocation()) - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMPublicIpDestroy, - Steps: []resource.TestStep{ - { - Config: config, - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateId: fmt.Sprintf("/subscriptions/%s/resourceGroups/acctestRG-%d/providers/Microsoft.Network/publicIPAdresses/acctestpublicip-%d", os.Getenv("ARM_SUBSCRIPTION_ID"), ri, ri), - ExpectError: regexp.MustCompile("Error parsing supplied resource id."), - }, - }, - }) -} diff --git a/azurerm/import_arm_virtual_network_gateway_connection_test.go b/azurerm/import_arm_virtual_network_gateway_connection_test.go deleted file mode 100644 index 074004b12f36..000000000000 --- a/azurerm/import_arm_virtual_network_gateway_connection_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package azurerm - -import ( - "testing" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" -) - -func TestAccAzureRMVirtualNetworkGatewayConnection_importSiteToSite(t *testing.T) { - resourceName := "azurerm_virtual_network_gateway_connection.test" - - ri := acctest.RandInt() - config := testAccAzureRMVirtualNetworkGatewayConnection_sitetosite(ri, testLocation()) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMVirtualNetworkGatewayConnectionDestroy, - Steps: []resource.TestStep{ - { - Config: config, - }, - - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} diff --git a/azurerm/provider.go b/azurerm/provider.go index 52647239fdf2..3b2908667350 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -141,6 +141,8 @@ func Provider() terraform.ResourceProvider { "azurerm_automation_schedule": resourceArmAutomationSchedule(), "azurerm_autoscale_setting": resourceArmAutoScaleSetting(), "azurerm_availability_set": resourceArmAvailabilitySet(), + "azurerm_firewall": resourceArmFirewall(), + "azurerm_firewall_network_rule_collection": resourceArmFirewallNetworkRuleCollection(), "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), "azurerm_cdn_profile": resourceArmCdnProfile(), "azurerm_container_registry": resourceArmContainerRegistry(), @@ -204,6 +206,7 @@ func Provider() terraform.ResourceProvider { "azurerm_mysql_database": resourceArmMySqlDatabase(), "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), "azurerm_mysql_server": resourceArmMySqlServer(), + "azurerm_mysql_virtual_network_rule": resourceArmMySqlVirtualNetworkRule(), "azurerm_network_interface": resourceArmNetworkInterface(), "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), diff --git a/azurerm/resource_arm_firewall.go b/azurerm/resource_arm_firewall.go new file mode 100644 index 000000000000..17103fd65c2f --- /dev/null +++ b/azurerm/resource_arm_firewall.go @@ -0,0 +1,332 @@ +package azurerm + +import ( + "fmt" + "log" + "regexp" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-04-01/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +var azureFirewallResourceName = "azurerm_firewall" + +func resourceArmFirewall() *schema.Resource { + return &schema.Resource{ + Create: resourceArmFirewallCreateUpdate, + Read: resourceArmFirewallRead, + Update: resourceArmFirewallCreateUpdate, + Delete: resourceArmFirewallDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureFirewallName, + }, + + "location": locationSchema(), + + "resource_group_name": resourceGroupNameSchema(), + + "ip_configuration": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "internal_public_ip_address_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "private_ip_address": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmFirewallCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM Azure Firewall creation") + + name := d.Get("name").(string) + location := azureRMNormalizeLocation(d.Get("location").(string)) + resourceGroup := d.Get("resource_group_name").(string) + tags := d.Get("tags").(map[string]interface{}) + ipConfigs, subnetToLock, vnetToLock, err := expandArmFirewallIPConfigurations(d) + if err != nil { + return fmt.Errorf("Error Building list of Azure Firewall IP Configurations: %+v", err) + } + + azureRMLockByName(name, azureFirewallResourceName) + defer azureRMUnlockByName(name, azureFirewallResourceName) + + azureRMLockMultipleByName(subnetToLock, subnetResourceName) + defer azureRMUnlockMultipleByName(subnetToLock, subnetResourceName) + + azureRMLockMultipleByName(vnetToLock, virtualNetworkResourceName) + defer azureRMUnlockMultipleByName(vnetToLock, virtualNetworkResourceName) + + parameters := network.AzureFirewall{ + Location: &location, + Tags: expandTags(tags), + AzureFirewallPropertiesFormat: &network.AzureFirewallPropertiesFormat{ + IPConfigurations: ipConfigs, + }, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters) + if err != nil { + return fmt.Errorf("Error creating/updating Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for creation/update of Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if read.ID == nil { + return fmt.Errorf("Cannot read Azure Firewall %q (Resource Group %q) ID", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmFirewallRead(d, meta) +} + +func resourceArmFirewallRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["azureFirewalls"] + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(read.Response) { + log.Printf("[DEBUG] Firewall %q was not found in Resource Group %q - removing from state!", name, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", read.Name) + d.Set("resource_group_name", resourceGroup) + if location := read.Location; location != nil { + d.Set("location", azureRMNormalizeLocation(*location)) + } + + if props := read.AzureFirewallPropertiesFormat; props != nil { + ipConfigs := flattenArmFirewallIPConfigurations(props.IPConfigurations) + if err := d.Set("ip_configuration", ipConfigs); err != nil { + return fmt.Errorf("Error setting `ip_configuration`: %+v", err) + } + } + + flattenAndSetTags(d, read.Tags) + + return nil +} + +func resourceArmFirewallDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["azureFirewalls"] + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(read.Response) { + // deleted outside of TF + log.Printf("[DEBUG] Firewall %q was not found in Resource Group %q - assuming removed!", name, resourceGroup) + return nil + } + + return fmt.Errorf("Error retrieving Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + subnetNamesToLock := make([]string, 0) + virtualNetworkNamesToLock := make([]string, 0) + if props := read.AzureFirewallPropertiesFormat; props != nil { + if configs := props.IPConfigurations; configs != nil { + for _, config := range *configs { + if config.Subnet == nil || config.Subnet.ID == nil { + continue + } + + parsedSubnetId, err := parseAzureResourceID(*config.Subnet.ID) + if err != nil { + return err + } + subnetName := parsedSubnetId.Path["subnets"] + + if !sliceContainsValue(subnetNamesToLock, subnetName) { + subnetNamesToLock = append(subnetNamesToLock, subnetName) + } + + virtualNetworkName := parsedSubnetId.Path["virtualNetworks"] + if !sliceContainsValue(virtualNetworkNamesToLock, virtualNetworkName) { + virtualNetworkNamesToLock = append(virtualNetworkNamesToLock, virtualNetworkName) + } + } + } + } + + azureRMLockByName(name, azureFirewallResourceName) + defer azureRMUnlockByName(name, azureFirewallResourceName) + + azureRMLockMultipleByName(&subnetNamesToLock, subnetResourceName) + defer azureRMUnlockMultipleByName(&subnetNamesToLock, subnetResourceName) + + azureRMLockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + defer azureRMUnlockMultipleByName(&virtualNetworkNamesToLock, virtualNetworkResourceName) + + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error deleting Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for the deletion of Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + return err +} + +func expandArmFirewallIPConfigurations(d *schema.ResourceData) (*[]network.AzureFirewallIPConfiguration, *[]string, *[]string, error) { + configs := d.Get("ip_configuration").([]interface{}) + ipConfigs := make([]network.AzureFirewallIPConfiguration, 0) + subnetNamesToLock := make([]string, 0) + virtualNetworkNamesToLock := make([]string, 0) + + for _, configRaw := range configs { + data := configRaw.(map[string]interface{}) + name := data["name"].(string) + subnetId := data["subnet_id"].(string) + intPubID := data["internal_public_ip_address_id"].(string) + + subnetID, err := parseAzureResourceID(subnetId) + if err != nil { + return nil, nil, nil, err + } + + subnetName := subnetID.Path["subnets"] + virtualNetworkName := subnetID.Path["virtualNetworks"] + + if !sliceContainsValue(subnetNamesToLock, subnetName) { + subnetNamesToLock = append(subnetNamesToLock, subnetName) + } + + if !sliceContainsValue(virtualNetworkNamesToLock, virtualNetworkName) { + virtualNetworkNamesToLock = append(virtualNetworkNamesToLock, virtualNetworkName) + } + + ipConfig := network.AzureFirewallIPConfiguration{ + Name: utils.String(name), + AzureFirewallIPConfigurationPropertiesFormat: &network.AzureFirewallIPConfigurationPropertiesFormat{ + Subnet: &network.SubResource{ + ID: utils.String(subnetId), + }, + InternalPublicIPAddress: &network.SubResource{ + ID: utils.String(intPubID), + }, + }, + } + ipConfigs = append(ipConfigs, ipConfig) + } + return &ipConfigs, &subnetNamesToLock, &virtualNetworkNamesToLock, nil +} + +func flattenArmFirewallIPConfigurations(input *[]network.AzureFirewallIPConfiguration) []interface{} { + result := make([]interface{}, 0) + if input == nil { + return result + } + + for _, v := range *input { + afIPConfig := make(map[string]interface{}) + props := v.AzureFirewallIPConfigurationPropertiesFormat + if props == nil { + continue + } + + if name := v.Name; name != nil { + afIPConfig["name"] = *name + } + + if subnet := props.Subnet; subnet != nil { + if id := subnet.ID; id != nil { + afIPConfig["subnet_id"] = *id + } + } + + if ipAddress := props.PrivateIPAddress; ipAddress != nil { + afIPConfig["private_ip_address"] = *ipAddress + } + + if pip := props.PublicIPAddress; pip != nil { + if id := pip.ID; id != nil { + afIPConfig["internal_public_ip_address_id"] = *id + } + } + result = append(result, afIPConfig) + } + + return result +} + +func validateAzureFirewallName(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + + // From the Portal: + // The name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. + if matched := regexp.MustCompile(`^[0-9a-zA-Z]([0-9a-zA-Z.\_-]{0,}[0-9a-zA-Z_])?$`).Match([]byte(value)); !matched { + es = append(es, fmt.Errorf("%q must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens.", k)) + } + + return +} diff --git a/azurerm/resource_arm_firewall_network_rule_collection.go b/azurerm/resource_arm_firewall_network_rule_collection.go new file mode 100644 index 000000000000..deae2138337a --- /dev/null +++ b/azurerm/resource_arm_firewall_network_rule_collection.go @@ -0,0 +1,436 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-04-01/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmFirewallNetworkRuleCollection() *schema.Resource { + return &schema.Resource{ + Create: resourceArmFirewallNetworkRuleCollectionCreateUpdate, + Read: resourceArmFirewallNetworkRuleCollectionRead, + Update: resourceArmFirewallNetworkRuleCollectionCreateUpdate, + Delete: resourceArmFirewallNetworkRuleCollectionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureFirewallName, + }, + + "azure_firewall_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAzureFirewallName, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "priority": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(100, 65000), + }, + + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.AzureFirewallRCActionTypeAllow), + string(network.AzureFirewallRCActionTypeDeny), + }, false), + }, + + "rule": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "source_addresses": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "destination_addresses": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "destination_ports": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "protocols": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(network.Any), + string(network.ICMP), + string(network.TCP), + string(network.UDP), + }, false), + }, + Set: schema.HashString, + }, + }, + }, + }, + }, + } +} + +func resourceArmFirewallNetworkRuleCollectionCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + firewallName := d.Get("azure_firewall_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + azureRMLockByName(firewallName, azureFirewallResourceName) + defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + + firewall, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + return fmt.Errorf("Error retrieving Firewall %q (Resource Group %q): %+v", firewallName, resourceGroup, err) + } + + if firewall.AzureFirewallPropertiesFormat == nil { + return fmt.Errorf("Error expanding Firewall %q (Resource Group %q): `properties` was nil.", firewallName, resourceGroup) + } + props := *firewall.AzureFirewallPropertiesFormat + + if props.NetworkRuleCollections == nil { + return fmt.Errorf("Error expanding Firewall %q (Resource Group %q): `properties.NetworkRuleCollections` was nil.", firewallName, resourceGroup) + } + ruleCollections := *props.NetworkRuleCollections + + ipConfigurations, err := azure.FirewallFixIPConfiguration(props.IPConfigurations) + if err != nil { + return fmt.Errorf("Error fixing IP Configurations for Firewall %q (Resource Group %q): %+v", firewallName, resourceGroup, err) + } + firewall.AzureFirewallPropertiesFormat.IPConfigurations = ipConfigurations + + networkRules := expandArmFirewallNetworkRules(d.Get("rule").(*schema.Set)) + priority := d.Get("priority").(int) + newRuleCollection := network.AzureFirewallNetworkRuleCollection{ + Name: utils.String(name), + AzureFirewallNetworkRuleCollectionPropertiesFormat: &network.AzureFirewallNetworkRuleCollectionPropertiesFormat{ + Action: &network.AzureFirewallRCAction{ + Type: network.AzureFirewallRCActionType(d.Get("action").(string)), + }, + Priority: utils.Int32(int32(priority)), + Rules: &networkRules, + }, + } + + if !d.IsNewResource() { + index := -1 + if ruleCollections != nil { + for i, v := range ruleCollections { + if v.Name == nil { + continue + } + + if *v.Name == name { + index = i + break + } + } + } + + if index == -1 { + return fmt.Errorf("Error locating Network Rule Collection %q (Firewall %q / Resource Group %q)", name, firewallName, resourceGroup) + } + + ruleCollections[index] = newRuleCollection + } else { + ruleCollections = append(ruleCollections, newRuleCollection) + } + + firewall.AzureFirewallPropertiesFormat.NetworkRuleCollections = &ruleCollections + + future, err := client.CreateOrUpdate(ctx, resourceGroup, firewallName, firewall) + if err != nil { + return fmt.Errorf("Error creating/updating Network Rule Collection %q in Firewall %q (Resource Group %q): %+v", name, firewallName, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for creation/update of Network Rule Collection %q of Firewall %q (Resource Group %q): %+v", name, firewallName, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + return fmt.Errorf("Error retrieving Firewall %q (Resource Group %q): %+v", firewallName, resourceGroup, err) + } + + var collectionID string + if props := read.AzureFirewallPropertiesFormat; props != nil { + if collections := props.NetworkRuleCollections; collections != nil { + for _, collection := range *collections { + if collection.Name == nil { + continue + } + + if *collection.Name == name { + collectionID = *collection.ID + break + } + } + } + } + + if collectionID == "" { + return fmt.Errorf("Cannot find ID for Network Rule Collection %q (Azure Firewall %q / Resource Group %q)", name, firewallName, resourceGroup) + } + d.SetId(collectionID) + + return resourceArmFirewallNetworkRuleCollectionRead(d, meta) +} + +func resourceArmFirewallNetworkRuleCollectionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + firewallName := id.Path["azureFirewalls"] + name := id.Path["networkRuleCollections"] + + read, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + if utils.ResponseWasNotFound(read.Response) { + log.Printf("[DEBUG] Azure Firewall %q (Resource Group %q) was not found - removing from state!", name, resourceGroup) + d.SetId("") + return nil + } + return fmt.Errorf("Error retrieving Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + if read.AzureFirewallPropertiesFormat == nil { + return fmt.Errorf("Error retrieving Network Rule Collection %q (Firewall %q / Resource Group %q): `props` was nil", name, firewallName, resourceGroup) + } + props := *read.AzureFirewallPropertiesFormat + + if props.NetworkRuleCollections == nil { + return fmt.Errorf("Error retrieving Network Rule Collection %q (Firewall %q / Resource Group %q): `props.NetworkRuleCollections` was nil", name, firewallName, resourceGroup) + } + + var rule *network.AzureFirewallNetworkRuleCollection + for _, r := range *props.NetworkRuleCollections { + if r.Name == nil { + continue + } + + if *r.Name == name { + rule = &r + break + } + } + + if rule == nil { + log.Printf("[DEBUG] Network Rule Collection %q was not found on Firewall %q (Resource Group %q) - removing from state!", name, firewallName, resourceGroup) + d.SetId("") + return nil + } + + d.Set("name", rule.Name) + d.Set("azure_firewall_name", firewallName) + d.Set("resource_group_name", resourceGroup) + + if props := rule.AzureFirewallNetworkRuleCollectionPropertiesFormat; props != nil { + if action := props.Action; action != nil { + d.Set("action", string(action.Type)) + } + + if priority := props.Priority; priority != nil { + d.Set("priority", int(*priority)) + } + + flattenedRules := flattenFirewallNetworkRuleCollectionRules(props.Rules) + if err := d.Set("rule", flattenedRules); err != nil { + return fmt.Errorf("Error setting `rule`: %+v", err) + } + } + + return nil +} + +func resourceArmFirewallNetworkRuleCollectionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).azureFirewallsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + firewallName := id.Path["azureFirewalls"] + name := id.Path["networkRuleCollections"] + + azureRMLockByName(firewallName, azureFirewallResourceName) + defer azureRMUnlockByName(firewallName, azureFirewallResourceName) + + firewall, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + if utils.ResponseWasNotFound(firewall.Response) { + // assume deleted + return nil + } + + return fmt.Errorf("Error making Read request on Azure Firewall %q (Resource Group %q): %+v", firewallName, resourceGroup, err) + } + + props := firewall.AzureFirewallPropertiesFormat + if props == nil { + return fmt.Errorf("Error retrieving Network Rule Collection %q (Firewall %q / Resource Group %q): `props` was nil", name, firewallName, resourceGroup) + } + if props.NetworkRuleCollections == nil { + return fmt.Errorf("Error retrieving Network Rule Collection %q (Firewall %q / Resource Group %q): `props.NetworkRuleCollections` was nil", name, firewallName, resourceGroup) + } + + networkRules := make([]network.AzureFirewallNetworkRuleCollection, 0) + for _, rule := range *props.NetworkRuleCollections { + if rule.Name == nil { + continue + } + + if *rule.Name != name { + networkRules = append(networkRules, rule) + } + } + props.NetworkRuleCollections = &networkRules + + ipConfigs, err := azure.FirewallFixIPConfiguration(props.IPConfigurations) + if err != nil { + return fmt.Errorf("Error fixing IP Configuration for Firewall %q (Resource Group %q): %+v", firewallName, resourceGroup, err) + } + props.IPConfigurations = ipConfigs + + future, err := client.CreateOrUpdate(ctx, resourceGroup, firewallName, firewall) + if err != nil { + return fmt.Errorf("Error deleting Network Rule Collection %q from Firewall %q (Resource Group %q): %+v", name, firewallName, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for deletion of Network Rule Collection %q from Firewall %q (Resource Group %q): %+v", name, firewallName, resourceGroup, err) + } + + return nil +} + +func expandArmFirewallNetworkRules(input *schema.Set) []network.AzureFirewallNetworkRule { + nwRules := input.List() + rules := make([]network.AzureFirewallNetworkRule, 0) + + for _, nwRule := range nwRules { + rule := nwRule.(map[string]interface{}) + + name := rule["name"].(string) + description := rule["description"].(string) + + sourceAddresses := make([]string, 0) + for _, v := range rule["source_addresses"].(*schema.Set).List() { + sourceAddresses = append(sourceAddresses, v.(string)) + } + + destinationAddresses := make([]string, 0) + for _, v := range rule["destination_addresses"].(*schema.Set).List() { + destinationAddresses = append(destinationAddresses, v.(string)) + } + + destinationPorts := make([]string, 0) + for _, v := range rule["destination_ports"].(*schema.Set).List() { + destinationPorts = append(destinationPorts, v.(string)) + } + + ruleToAdd := network.AzureFirewallNetworkRule{ + Name: utils.String(name), + Description: utils.String(description), + SourceAddresses: &sourceAddresses, + DestinationAddresses: &destinationAddresses, + DestinationPorts: &destinationPorts, + } + + nrProtocols := make([]network.AzureFirewallNetworkRuleProtocol, 0) + protocols := rule["protocols"].(*schema.Set) + for _, v := range protocols.List() { + s := network.AzureFirewallNetworkRuleProtocol(v.(string)) + nrProtocols = append(nrProtocols, s) + } + ruleToAdd.Protocols = &nrProtocols + rules = append(rules, ruleToAdd) + } + + return rules +} + +func flattenFirewallNetworkRuleCollectionRules(rules *[]network.AzureFirewallNetworkRule) []map[string]interface{} { + outputs := make([]map[string]interface{}, 0) + if rules == nil { + return outputs + } + + for _, rule := range *rules { + output := make(map[string]interface{}) + if rule.Name != nil { + output["name"] = *rule.Name + } + if rule.Description != nil { + output["description"] = *rule.Description + } + if rule.SourceAddresses != nil { + output["source_addresses"] = sliceToSet(*rule.SourceAddresses) + } + if rule.DestinationAddresses != nil { + output["destination_addresses"] = sliceToSet(*rule.DestinationAddresses) + } + if rule.DestinationPorts != nil { + output["destination_ports"] = sliceToSet(*rule.DestinationPorts) + } + protocols := make([]string, 0) + if rule.Protocols != nil { + for _, protocol := range *rule.Protocols { + protocols = append(protocols, string(protocol)) + } + } + output["protocols"] = sliceToSet(protocols) + outputs = append(outputs, output) + } + return outputs +} diff --git a/azurerm/resource_arm_firewall_network_rule_collection_test.go b/azurerm/resource_arm_firewall_network_rule_collection_test.go new file mode 100644 index 000000000000..83c4f388a64a --- /dev/null +++ b/azurerm/resource_arm_firewall_network_rule_collection_test.go @@ -0,0 +1,606 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-04-01/network" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +func TestAccAzureRMFirewallNetworkRuleCollection_basic(t *testing.T) { + resourceName := "azurerm_firewall_network_rule_collection.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFirewallNetworkRuleCollection_updatedName(t *testing.T) { + resourceName := "azurerm_firewall_network_rule_collection.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.3765122797.name", "rule1"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_updatedName(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule.1700340761.name", "rule2"), + ), + }, + }, + }) +} + +func TestAccAzureRMFirewallNetworkRuleCollection_multipleRuleCollections(t *testing.T) { + firstRule := "azurerm_firewall_network_rule_collection.test" + secondRule := "azurerm_firewall_network_rule_collection.test_add" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(firstRule), + resource.TestCheckResourceAttr(firstRule, "name", "acctestnrc"), + resource.TestCheckResourceAttr(firstRule, "priority", "100"), + resource.TestCheckResourceAttr(firstRule, "action", "Allow"), + resource.TestCheckResourceAttr(firstRule, "rule.#", "1"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_multiple(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(firstRule), + resource.TestCheckResourceAttr(firstRule, "name", "acctestnrc"), + resource.TestCheckResourceAttr(firstRule, "priority", "100"), + resource.TestCheckResourceAttr(firstRule, "action", "Allow"), + resource.TestCheckResourceAttr(firstRule, "rule.#", "1"), + testCheckAzureRMFirewallNetworkRuleCollectionExists(secondRule), + resource.TestCheckResourceAttr(secondRule, "name", "acctestnrc_add"), + resource.TestCheckResourceAttr(secondRule, "priority", "200"), + resource.TestCheckResourceAttr(secondRule, "action", "Deny"), + resource.TestCheckResourceAttr(secondRule, "rule.#", "1"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(firstRule), + resource.TestCheckResourceAttr(firstRule, "name", "acctestnrc"), + resource.TestCheckResourceAttr(firstRule, "priority", "100"), + resource.TestCheckResourceAttr(firstRule, "action", "Allow"), + resource.TestCheckResourceAttr(firstRule, "rule.#", "1"), + testCheckAzureRMFirewallNetworkRuleCollectionDoesNotExist("azurerm_firewall.test", "acctestnrc_add"), + ), + }, + }, + }) +} + +func TestAccAzureRMFirewallNetworkRuleCollection_update(t *testing.T) { + firstResourceName := "azurerm_firewall_network_rule_collection.test" + secondResourceName := "azurerm_firewall_network_rule_collection.test_add" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_multiple(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(firstResourceName), + resource.TestCheckResourceAttr(firstResourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(firstResourceName, "priority", "100"), + resource.TestCheckResourceAttr(firstResourceName, "action", "Allow"), + resource.TestCheckResourceAttr(firstResourceName, "rule.#", "1"), + testCheckAzureRMFirewallNetworkRuleCollectionExists(secondResourceName), + resource.TestCheckResourceAttr(secondResourceName, "name", "acctestnrc_add"), + resource.TestCheckResourceAttr(secondResourceName, "priority", "200"), + resource.TestCheckResourceAttr(secondResourceName, "action", "Deny"), + resource.TestCheckResourceAttr(secondResourceName, "rule.#", "1"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_multipleUpdate(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(firstResourceName), + resource.TestCheckResourceAttr(firstResourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(firstResourceName, "priority", "300"), + resource.TestCheckResourceAttr(firstResourceName, "action", "Deny"), + resource.TestCheckResourceAttr(firstResourceName, "rule.#", "1"), + testCheckAzureRMFirewallNetworkRuleCollectionExists(secondResourceName), + resource.TestCheckResourceAttr(secondResourceName, "name", "acctestnrc_add"), + resource.TestCheckResourceAttr(secondResourceName, "priority", "400"), + resource.TestCheckResourceAttr(secondResourceName, "action", "Allow"), + resource.TestCheckResourceAttr(secondResourceName, "rule.#", "1"), + ), + }, + }, + }) +} + +func TestAccAzureRMFirewallNetworkRuleCollection_disappears(t *testing.T) { + resourceName := "azurerm_firewall_network_rule_collection.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + testCheckAzureRMFirewallNetworkRuleCollectionDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMFirewallNetworkRuleCollection_multipleRules(t *testing.T) { + resourceName := "azurerm_firewall_network_rule_collection.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_multipleRules(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "2"), + ), + }, + { + Config: testAccAzureRMFirewallNetworkRuleCollection_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", "acctestnrc"), + resource.TestCheckResourceAttr(resourceName, "priority", "100"), + resource.TestCheckResourceAttr(resourceName, "action", "Allow"), + resource.TestCheckResourceAttr(resourceName, "rule.#", "1"), + ), + }, + }, + }) +} + +func testCheckAzureRMFirewallNetworkRuleCollectionExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + firewallName := rs.Primary.Attributes["azure_firewall_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + read, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + return err + } + + found := false + for _, collection := range *read.AzureFirewallPropertiesFormat.NetworkRuleCollections { + if *collection.Name == name { + found = true + break + } + } + + if !found { + return fmt.Errorf("Expected Network Rule Collection %q (Firewall %q / Resource Group %q) to exist but it didn't", name, firewallName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMFirewallNetworkRuleCollectionDoesNotExist(resourceName string, collectionName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + firewallName := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + read, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + return err + } + + for _, collection := range *read.AzureFirewallPropertiesFormat.NetworkRuleCollections { + if *collection.Name == collectionName { + return fmt.Errorf("Network Rule Collection %q exists in Firewall %q: %+v", collectionName, firewallName, collection) + } + } + + return nil + } +} + +func testCheckAzureRMFirewallNetworkRuleCollectionDisappears(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + firewallName := rs.Primary.Attributes["azure_firewall_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + read, err := client.Get(ctx, resourceGroup, firewallName) + if err != nil { + return err + } + + rules := make([]network.AzureFirewallNetworkRuleCollection, 0) + for _, collection := range *read.AzureFirewallPropertiesFormat.NetworkRuleCollections { + if *collection.Name != name { + rules = append(rules, collection) + } + } + + read.AzureFirewallPropertiesFormat.NetworkRuleCollections = &rules + ipConfigs, err := azure.FirewallFixIPConfiguration(read.AzureFirewallPropertiesFormat.IPConfigurations) + if err != nil { + return fmt.Errorf("Error fixing IP Configuration for Firewall: err") + } + + read.AzureFirewallPropertiesFormat.IPConfigurations = ipConfigs + + future, err := client.CreateOrUpdate(ctx, resourceGroup, firewallName, read) + if err != nil { + return fmt.Errorf("Error removing Network Rule Collection from Firewall: %+v", err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for the removal of Network Rule Collection from Firewall: %+v", err) + } + + _, err = client.Get(ctx, resourceGroup, firewallName) + return err + } +} + +func testAccAzureRMFirewallNetworkRuleCollection_basic(rInt int, location string) string { + template := testAccAzureRMFirewall_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "acctestnrc" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 100 + action = "Allow" + + rule { + name = "rule1" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + ] + + protocols = [ + "Any", + ] + } +} +`, template) +} + +func testAccAzureRMFirewallNetworkRuleCollection_updatedName(rInt int, location string) string { + template := testAccAzureRMFirewall_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "acctestnrc" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 100 + action = "Allow" + + rule { + name = "rule2" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + ] + + protocols = [ + "Any", + ] + } +} +`, template) +} + +func testAccAzureRMFirewallNetworkRuleCollection_multiple(rInt int, location string) string { + template := testAccAzureRMFirewall_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "acctestnrc" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 100 + action = "Allow" + + rule { + name = "acctestrule" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + ] + + protocols = [ + "Any", + ] + } +} + +resource "azurerm_firewall_network_rule_collection" "test_add" { + name = "acctestnrc_add" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 200 + action = "Deny" + + rule { + name = "acctestruleadd" + + source_addresses = [ + "10.0.0.0/8", + ] + + destination_ports = [ + "8080", + ] + + destination_addresses = [ + "8.8.4.4", + ] + + protocols = [ + "TCP", + ] + } +} +`, template) +} + +func testAccAzureRMFirewallNetworkRuleCollection_multipleUpdate(rInt int, location string) string { + template := testAccAzureRMFirewall_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "acctestnrc" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 300 + action = "Deny" + + rule { + name = "acctestrule" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + ] + + protocols = [ + "Any", + ] + } +} + +resource "azurerm_firewall_network_rule_collection" "test_add" { + name = "acctestnrc_add" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 400 + action = "Allow" + + rule { + name = "acctestruleadd" + + source_addresses = [ + "10.0.0.0/8", + ] + + destination_ports = [ + "8080", + ] + + destination_addresses = [ + "8.8.4.4", + ] + + protocols = [ + "TCP", + ] + } +} +`, template) +} + +func testAccAzureRMFirewallNetworkRuleCollection_multipleRules(rInt int, location string) string { + template := testAccAzureRMFirewall_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "acctestnrc" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 100 + action = "Allow" + + rule { + name = "acctestrule" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + ] + + protocols = [ + "Any", + ] + } + + rule { + name = "acctestrule_add" + + source_addresses = [ + "192.168.0.1", + ] + + destination_ports = [ + "8888", + ] + + destination_addresses = [ + "1.1.1.1", + ] + + protocols = [ + "TCP", + ] + } +} +`, template) +} diff --git a/azurerm/resource_arm_firewall_test.go b/azurerm/resource_arm_firewall_test.go new file mode 100644 index 000000000000..4ce044c257a8 --- /dev/null +++ b/azurerm/resource_arm_firewall_test.go @@ -0,0 +1,357 @@ +package azurerm + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + + "github.com/hashicorp/terraform/helper/acctest" +) + +func TestValidateFirewallName(t *testing.T) { + // The name must begin with a letter or number, end with a letter, number or underscore, and may contain only letters, numbers, underscores, periods, or hyphens. + validNames := []string{ + "a", + "abc123", + "a_b_c", + "hy-ph-en", + "valid_", + "v-a_l1.d_", + strings.Repeat("w", 65), + } + for _, v := range validNames { + _, errors := validateAzureFirewallName(v, "name") + if len(errors) != 0 { + t.Fatalf("%q should be a valid Firewall Name: %q", v, errors) + } + } + + invalidNames := []string{ + "_invalid", + "-invalid", + ".invalid", + "!invalid", + "hel!!o", + "invalid.", + "invalid-", + "invalid!", + } + for _, v := range invalidNames { + _, errors := validateAzureFirewallName(v, "name") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid Firewall Name", v) + } + } +} + +func TestAccAzureRMFirewall_basic(t *testing.T) { + resourceName := "azurerm_firewall.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewall_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "ip_configuration.0.name", "configuration"), + resource.TestCheckResourceAttrSet(resourceName, "ip_configuration.0.private_ip_address"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFirewall_withTags(t *testing.T) { + resourceName := "azurerm_firewall.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewall_withTags(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "Production"), + resource.TestCheckResourceAttr(resourceName, "tags.cost_center", "MSFT"), + ), + }, + { + Config: testAccAzureRMFirewall_withUpdatedTags(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "staging"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMFirewall_disappears(t *testing.T) { + resourceName := "azurerm_firewall.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMFirewall_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMFirewallExists(resourceName), + testCheckAzureRMFirewallDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testCheckAzureRMFirewallExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Azure Firewall: %q", name) + } + + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Azure Firewall %q (Resource Group: %q) does not exist", name, resourceGroup) + } + + return fmt.Errorf("Bad: Get on azureFirewallsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMFirewallDisappears(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Azure Firewall: %q", name) + } + + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + future, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Bad: Delete on azureFirewallsClient: %+v", err) + } + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Bad: waiting for Deletion on azureFirewallsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMFirewallDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).azureFirewallsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_firewall" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Firewall still exists:\n%#v", resp.AzureFirewallPropertiesFormat) + } + + return nil +} + +func testAccAzureRMFirewall_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%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 = "AzureFirewallSubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctestpip%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "test" { + name = "acctestfirewall%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "configuration" + subnet_id = "${azurerm_subnet.test.id}" + internal_public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMFirewall_withTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%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 = "AzureFirewallSubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctestpip%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "test" { + name = "acctestfirewall%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "configuration" + subnet_id = "${azurerm_subnet.test.id}" + internal_public_ip_address_id = "${azurerm_public_ip.test.id}" + } + + tags { + environment = "Production" + cost_center = "MSFT" + } +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMFirewall_withUpdatedTags(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%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 = "AzureFirewallSubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "acctestpip%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "test" { + name = "acctestfirewall%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "configuration" + subnet_id = "${azurerm_subnet.test.id}" + internal_public_ip_address_id = "${azurerm_public_ip.test.id}" + } + + tags { + environment = "staging" + } +} +`, rInt, location, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_key_vault_certificate.go b/azurerm/resource_arm_key_vault_certificate.go index 9b82a13e3b56..c6fb7e407970 100644 --- a/azurerm/resource_arm_key_vault_certificate.go +++ b/azurerm/resource_arm_key_vault_certificate.go @@ -2,8 +2,11 @@ package azurerm import ( "context" + "encoding/base64" + "encoding/hex" "fmt" "log" + "strings" "time" "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault" @@ -236,6 +239,11 @@ func resourceArmKeyVaultCertificate() *schema.Resource { Computed: true, }, + "thumbprint": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), }, } @@ -349,6 +357,15 @@ func resourceArmKeyVaultCertificateRead(d *schema.ResourceData, meta interface{} if contents := cert.Cer; contents != nil { d.Set("certificate_data", string(*contents)) } + + if v := cert.X509Thumbprint; v != nil { + x509Thumbprint, err := base64.RawURLEncoding.DecodeString(string(*v)) + if err != nil { + return err + } + d.Set("thumbprint", strings.ToUpper(hex.EncodeToString(x509Thumbprint))) + } + flattenAndSetTags(d, cert.Tags) return nil diff --git a/azurerm/resource_arm_log_analytics_solution.go b/azurerm/resource_arm_log_analytics_solution.go index f06074a1d32f..cba504fd2c26 100644 --- a/azurerm/resource_arm_log_analytics_solution.go +++ b/azurerm/resource_arm_log_analytics_solution.go @@ -6,8 +6,10 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/services/preview/operationsmanagement/mgmt/2015-11-01-preview/operationsmanagement" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -24,9 +26,24 @@ func resourceArmLogAnalyticsSolution() *schema.Resource { Schema: map[string]*schema.Schema{ "solution_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + + "workspace_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + + "workspace_resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: suppress.CaseDifference, }, "location": locationSchema(), @@ -61,19 +78,6 @@ func resourceArmLogAnalyticsSolution() *schema.Resource { }, }, }, - - "workspace_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "workspace_resource_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, - }, }, } } @@ -81,7 +85,7 @@ func resourceArmLogAnalyticsSolution() *schema.Resource { func resourceArmLogAnalyticsSolutionCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).solutionsClient ctx := meta.(*ArmClient).StopContext - log.Printf("[INFO] preparing arguments for AzureRM Log Analytics solution creation.") + log.Printf("[INFO] preparing arguments for Log Analytics Solution creation.") // The resource requires both .name and .plan.name are set in the format // "SolutionName(WorkspaceName)". Feedback will be submitted to the OMS team as IMO this isn't ideal. @@ -94,23 +98,22 @@ func resourceArmLogAnalyticsSolutionCreateUpdate(d *schema.ResourceData, meta in workspaceID := d.Get("workspace_resource_id").(string) parameters := operationsmanagement.Solution{ - Name: &name, - Location: &location, + Name: utils.String(name), + Location: utils.String(location), Plan: &solutionPlan, Properties: &operationsmanagement.SolutionProperties{ - WorkspaceResourceID: &workspaceID, + WorkspaceResourceID: utils.String(workspaceID), }, } - res, err := client.CreateOrUpdate(ctx, resGroup, name, parameters) - //Currently this is required to work around successful creation resulting in an error - // being returned - if err != nil && res.Response().StatusCode != 201 { - if resp := res.Response(); resp != nil { - if resp.StatusCode != 201 { - return err - } - } + future, err := client.CreateOrUpdate(ctx, resGroup, name, parameters) + if err != nil { + return fmt.Errorf("Error creating/updating Log Analytics Solution %q (Workspace %q / Resource Group %q): %+v", name, workspaceID, resGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + return fmt.Errorf("Error waiting for the create/update of Log Analytics Solution %q (Workspace %q / Resource Group %q): %+v", name, workspaceID, resGroup, err) } solution, err := client.Get(ctx, resGroup, name) @@ -158,25 +161,27 @@ func resourceArmLogAnalyticsSolutionRead(d *schema.ResourceData, meta interface{ // Reversing the mapping used to get .solution_name // expecting resp.Name to be in format "SolutionName(WorkspaceName)". - if resp.Name != nil && strings.Contains(*resp.Name, "(") { - if parts := strings.Split(*resp.Name, "("); len(parts) == 2 { - d.Set("solution_name", parts[0]) - workspaceName := strings.TrimPrefix(parts[1], "(") - workspaceName = strings.TrimSuffix(workspaceName, ")") - d.Set("workspace_name", workspaceName) - } else { - return fmt.Errorf("Error making Read request on AzureRM Log Analytics solutions '%v': isn't in expected format 'Solution(WorkspaceName)'", resp.Name) + if v := resp.Name; v != nil { + val := *v + segments := strings.Split(*v, "(") + if len(segments) != 2 { + return fmt.Errorf("Expected %q to match 'Solution(WorkspaceName)'", val) } - } else { - return fmt.Errorf("Error making Read request on AzureRM Log Analytics solutions '%v': isn't in expected format 'Solution(WorkspaceName)'", resp.Name) + + solutionName := segments[0] + workspaceName := strings.TrimSuffix(segments[1], ")") + d.Set("solution_name", solutionName) + d.Set("workspace_name", workspaceName) } if props := resp.Properties; props != nil { d.Set("workspace_resource_id", props.WorkspaceResourceID) } - if plan := resp.Plan; plan != nil { - d.Set("plan", flattenAzureRmLogAnalyticsSolutionPlan(*resp.Plan)) + + if err := d.Set("plan", flattenAzureRmLogAnalyticsSolutionPlan(resp.Plan)); err != nil { + return fmt.Errorf("Error setting `plan`: %+v", err) } + return nil } @@ -197,11 +202,9 @@ func resourceArmLogAnalyticsSolutionDelete(d *schema.ResourceData, meta interfac err = future.WaitForCompletionRef(ctx, client.Client) if err != nil { - if response.WasNotFound(future.Response()) { - return nil + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deletion of Log Analytics Solution %q (Resource Group %q): %+v", name, resGroup, err) } - - return fmt.Errorf("Error waiting for deletion of Log Analytics Solution %q (Resource Group %q): %+v", name, resGroup, err) } return nil @@ -226,14 +229,29 @@ func expandAzureRmLogAnalyticsSolutionPlan(d *schema.ResourceData) operationsman return expandedPlan } -func flattenAzureRmLogAnalyticsSolutionPlan(plan operationsmanagement.SolutionPlan) []interface{} { - plans := make([]interface{}, 0) +func flattenAzureRmLogAnalyticsSolutionPlan(input *operationsmanagement.SolutionPlan) []interface{} { + output := make([]interface{}, 0) + if input == nil { + return output + } + values := make(map[string]interface{}) - values["name"] = *plan.Name - values["product"] = *plan.Product - values["promotion_code"] = *plan.PromotionCode - values["publisher"] = *plan.Publisher + if input.Name != nil { + values["name"] = *input.Name + } + + if input.Product != nil { + values["product"] = *input.Product + } + + if input.PromotionCode != nil { + values["promotion_code"] = *input.PromotionCode + } + + if input.Publisher != nil { + values["publisher"] = *input.Publisher + } - return append(plans, values) + return append(output, values) } diff --git a/azurerm/resource_arm_logic_app_trigger_http_request.go b/azurerm/resource_arm_logic_app_trigger_http_request.go index 43722012b36e..bcd7346f2c41 100644 --- a/azurerm/resource_arm_logic_app_trigger_http_request.go +++ b/azurerm/resource_arm_logic_app_trigger_http_request.go @@ -191,9 +191,9 @@ func resourceArmLogicAppTriggerHttpRequestDelete(d *schema.ResourceData, meta in func validateLogicAppTriggerHttpRequestRelativePath(v interface{}, k string) (ws []string, errors []error) { value := v.(string) - r, _ := regexp.Compile("^[A-Za-z0-9_]+$") + r, _ := regexp.Compile("^[A-Za-z0-9_/}{]+$") if !r.MatchString(value) { - errors = append(errors, fmt.Errorf("Relative Path can only contain alphanumeric characters and underscores.")) + errors = append(errors, fmt.Errorf("Relative Path can only contain alphanumeric characters, underscores, forward slashes and curly braces.")) } return diff --git a/azurerm/resource_arm_logic_app_trigger_http_request_test.go b/azurerm/resource_arm_logic_app_trigger_http_request_test.go index 899e44b5154c..981b14323b6d 100644 --- a/azurerm/resource_arm_logic_app_trigger_http_request_test.go +++ b/azurerm/resource_arm_logic_app_trigger_http_request_test.go @@ -82,7 +82,7 @@ func TestAccAzureRMLogicAppTriggerHttpRequest_relativePath(t *testing.T) { Check: resource.ComposeTestCheckFunc( testCheckAzureRMLogicAppTriggerExists(resourceName), resource.TestCheckResourceAttr(resourceName, "method", "POST"), - resource.TestCheckResourceAttr(resourceName, "relative_path", "hello_there"), + resource.TestCheckResourceAttr(resourceName, "relative_path", "customers/{id}"), ), }, }, @@ -178,7 +178,7 @@ resource "azurerm_logic_app_trigger_http_request" "test" { logic_app_id = "${azurerm_logic_app_workflow.test.id}" schema = "{}" method = "POST" - relative_path = "hello_there" + relative_path = "customers/{id}" } `, template) } diff --git a/azurerm/resource_arm_managed_disk.go b/azurerm/resource_arm_managed_disk.go index 5db396cd7193..9449f83474f6 100644 --- a/azurerm/resource_arm_managed_disk.go +++ b/azurerm/resource_arm_managed_disk.go @@ -41,6 +41,7 @@ func resourceArmManagedDisk() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(compute.StorageAccountTypesStandardLRS), string(compute.StorageAccountTypesPremiumLRS), + string(compute.StorageAccountTypesStandardSSDLRS), }, true), DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, @@ -124,11 +125,12 @@ func resourceArmManagedDiskCreate(d *schema.ResourceData, meta interface{}) erro zones := expandZones(d.Get("zones").([]interface{})) var skuName compute.StorageAccountTypes - // TODO: support for the StandardSSD if strings.EqualFold(storageAccountType, string(compute.StorageAccountTypesPremiumLRS)) { skuName = compute.StorageAccountTypesPremiumLRS - } else { + } else if strings.EqualFold(storageAccountType, string(compute.StorageAccountTypesStandardLRS)) { skuName = compute.StorageAccountTypesStandardLRS + } else if strings.EqualFold(storageAccountType, string(compute.StorageAccountTypesStandardSSDLRS)) { + skuName = compute.StorageAccountTypesStandardSSDLRS } createDisk := compute.Disk{ diff --git a/azurerm/resource_arm_managed_disk_test.go b/azurerm/resource_arm_managed_disk_test.go index 74f1bb63d58a..091f45ded2f9 100644 --- a/azurerm/resource_arm_managed_disk_test.go +++ b/azurerm/resource_arm_managed_disk_test.go @@ -139,7 +139,7 @@ func TestAccAzureRMManagedDisk_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.environment", "acctest"), resource.TestCheckResourceAttr(resourceName, "tags.cost-center", "ops"), resource.TestCheckResourceAttr(resourceName, "disk_size_gb", "1"), - resource.TestCheckResourceAttr(resourceName, "storage_account_type", string(compute.StandardLRS)), + resource.TestCheckResourceAttr(resourceName, "storage_account_type", string(compute.StorageAccountTypesStandardLRS)), ), }, { @@ -149,7 +149,7 @@ func TestAccAzureRMManagedDisk_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.environment", "acctest"), resource.TestCheckResourceAttr(resourceName, "disk_size_gb", "2"), - resource.TestCheckResourceAttr(resourceName, "storage_account_type", string(compute.PremiumLRS)), + resource.TestCheckResourceAttr(resourceName, "storage_account_type", string(compute.StorageAccountTypesPremiumLRS)), ), }, }, diff --git a/azurerm/resource_arm_mysql_virtual_network_rule.go b/azurerm/resource_arm_mysql_virtual_network_rule.go new file mode 100644 index 000000000000..14482108b8d9 --- /dev/null +++ b/azurerm/resource_arm_mysql_virtual_network_rule.go @@ -0,0 +1,228 @@ +package azurerm + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmMySqlVirtualNetworkRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMySqlVirtualNetworkRuleCreateUpdate, + Read: resourceArmMySqlVirtualNetworkRuleRead, + Update: resourceArmMySqlVirtualNetworkRuleCreateUpdate, + Delete: resourceArmMySqlVirtualNetworkRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.VirtualNetworkRuleName, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + + "subnet_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + } +} + +func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + serverName := d.Get("server_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + subnetId := d.Get("subnet_id").(string) + + // due to a bug in the API we have to ensure the Subnet's configured correctly or the API call will timeout + // BUG: https://github.com/Azure/azure-rest-api-specs/issues/3719 + subnetsClient := meta.(*ArmClient).subnetClient + subnetParsedId, err := parseAzureResourceID(subnetId) + + subnetResourceGroup := subnetParsedId.ResourceGroup + virtualNetwork := subnetParsedId.Path["virtualNetworks"] + subnetName := subnetParsedId.Path["subnets"] + subnet, err := subnetsClient.Get(ctx, subnetResourceGroup, virtualNetwork, subnetName, "") + if err != nil { + if utils.ResponseWasNotFound(subnet.Response) { + return fmt.Errorf("Subnet with ID %q was not found: %+v", subnetId, err) + } + + return fmt.Errorf("Error obtaining Subnet %q (Virtual Network %q / Resource Group %q: %+v", subnetName, virtualNetwork, subnetResourceGroup, err) + } + + containsEndpoint := false + if props := subnet.SubnetPropertiesFormat; props != nil { + if endpoints := props.ServiceEndpoints; endpoints != nil { + for _, e := range *endpoints { + if e.Service == nil { + continue + } + + if strings.EqualFold(*e.Service, "Microsoft.Sql") { + containsEndpoint = true + break + } + } + } + } + + if !containsEndpoint { + return fmt.Errorf("Error creating MySQL Virtual Network Rule: Subnet %q (Virtual Network %q / Resource Group %q) must contain a Service Endpoint for `Microsoft.Sql`", subnetName, virtualNetwork, subnetResourceGroup) + } + + parameters := mysql.VirtualNetworkRule{ + VirtualNetworkRuleProperties: &mysql.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetID: utils.String(subnetId), + IgnoreMissingVnetServiceEndpoint: utils.Bool(false), + }, + } + + _, err = client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + //Wait for the provisioning state to become ready + log.Printf("[DEBUG] Waiting for MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) to become ready: %+v", name, serverName, resourceGroup, err) + stateConf := &resource.StateChangeConf{ + Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, + Target: []string{"Ready"}, + Refresh: mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), + Timeout: 30 * time.Minute, + MinTimeout: 1 * time.Minute, + ContinuousTargetOccurence: 5, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmMySqlVirtualNetworkRuleRead(d, meta) +} + +func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["virtualNetworkRules"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading MySQL Virtual Network Rule %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading MySQL Virtual Network Rule: %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + + if props := resp.VirtualNetworkRuleProperties; props != nil { + d.Set("subnet_id", props.VirtualNetworkSubnetID) + } + + return nil +} + +func resourceArmMySqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["virtualNetworkRules"] + + future, err := client.Delete(ctx, resourceGroup, serverName, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deletion of MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + } + + return nil +} + +func mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client mysql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := client.Get(ctx, resourceGroup, serverName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned 404.", resourceGroup, serverName, name) + return nil, "ResponseNotFound", nil + } + + return nil, "", fmt.Errorf("Error polling for the state of the MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + if props := resp.VirtualNetworkRuleProperties; props != nil { + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned Status %s", resourceGroup, serverName, name, props.State) + return resp, fmt.Sprintf("%s", props.State), nil + } + + //Valid response was returned but VirtualNetworkRuleProperties was nil. Basically the rule exists, but with no properties for some reason. Assume Unknown instead of returning error. + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned empty VirtualNetworkRuleProperties", resourceGroup, serverName, name) + return resp, "Unknown", nil + } +} diff --git a/azurerm/resource_arm_mysql_virtual_network_rule_test.go b/azurerm/resource_arm_mysql_virtual_network_rule_test.go new file mode 100644 index 000000000000..a52836db2ae8 --- /dev/null +++ b/azurerm/resource_arm_mysql_virtual_network_rule_test.go @@ -0,0 +1,476 @@ +package azurerm + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMySqlVirtualNetworkRule_basic(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMySqlVirtualNetworkRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_switchSubnets(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + + preConfig := testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(ri, testLocation()) + postConfig := testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(ri, testLocation()) + + // Create regex strings that will ensure that one subnet name exists, but not the other + preConfigRegex := regexp.MustCompile(fmt.Sprintf("(subnet1%d)$|(subnet[^2]%d)$", ri, ri)) //subnet 1 but not 2 + postConfigRegex := regexp.MustCompile(fmt.Sprintf("(subnet2%d)$|(subnet[^1]%d)$", ri, ri)) //subnet 2 but not 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", preConfigRegex), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", postConfigRegex), + ), + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_disappears(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + config := testAccAzureRMMySqlVirtualNetworkRule_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + testCheckAzureRMMySqlVirtualNetworkRuleDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(t *testing.T) { + resourceName1 := "azurerm_mysql_virtual_network_rule.rule1" + resourceName2 := "azurerm_mysql_virtual_network_rule.rule2" + resourceName3 := "azurerm_mysql_virtual_network_rule.rule3" + ri := acctest.RandInt() + config := testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName1), + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName2), + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName3), + ), + }, + }, + }) +} + +func testCheckAzureRMMySqlVirtualNetworkRuleExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: MySql Virtual Network Rule %q (Server %q / Resource Group %q) was not found", ruleName, serverName, resourceGroup) + } + + return err + } + + return nil + } +} + +func testCheckAzureRMMySqlVirtualNetworkRuleDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_mysql_virtual_network_rule" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Bad: MySql Firewall Rule %q (Server %q / Resource Group %q) still exists: %+v", ruleName, serverName, resourceGroup, resp) + } + + return nil +} + +func testCheckAzureRMMySqlVirtualNetworkRuleDisappears(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + future, err := client.Delete(ctx, resourceGroup, serverName, ruleName) + if err != nil { + //If the error is that the resource we want to delete does not exist in the first + //place (404), then just return with no error. + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySql Virtual Network Rule: %+v", err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + //Same deal as before. Just in case. + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySql Virtual Network Rule: %+v", err) + } + + return nil + } +} + +func testAccAzureRMMySqlVirtualNetworkRule_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test1" { + name = "subnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "test2" { + name = "subnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.128/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test1.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test1" { + name = "subnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "test2" { + name = "subnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.128/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test2.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "vnet1" { + name = "acctestvnet1%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "vnet2" { + name = "acctestvnet2%d" + address_space = ["10.1.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "vnet1_subnet1" { + name = "acctestsubnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet1.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "vnet1_subnet2" { + name = "acctestsubnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet1.name}" + address_prefix = "10.7.29.128/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "vnet2_subnet1" { + name = "acctestsubnet3%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet2.name}" + address_prefix = "10.1.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "rule1" { + name = "acctestmysqlvnetrule1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet1.id}" +} + +resource "azurerm_mysql_virtual_network_rule" "rule2" { + name = "acctestmysqlvnetrule2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet2.id}" +} + +resource "azurerm_mysql_virtual_network_rule" "rule3" { + name = "acctestmysqlvnetrule3%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet2_subnet1.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_public_ip.go b/azurerm/resource_arm_public_ip.go index 8663880a353e..846338adaac9 100644 --- a/azurerm/resource_arm_public_ip.go +++ b/azurerm/resource_arm_public_ip.go @@ -2,6 +2,7 @@ package azurerm import ( "fmt" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "log" "regexp" "strings" @@ -35,9 +36,10 @@ func resourceArmPublicIp() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, }, "location": locationSchema(), @@ -46,33 +48,34 @@ func resourceArmPublicIp() *schema.Resource { "zones": singleZonesSchema(), - //should this perhaps be allocation_method? + //should this perhaps be allocation_method? (yes i think so) "public_ip_address_allocation": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + StateFunc: ignoreCaseStateFunc, ValidateFunc: validation.StringInSlice([]string{ string(network.Static), string(network.Dynamic), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, - StateFunc: ignoreCaseStateFunc, }, "sku": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: string(network.PublicIPAddressSkuNameBasic), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: string(network.PublicIPAddressSkuNameBasic), + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.PublicIPAddressSkuNameBasic), string(network.PublicIPAddressSkuNameStandard), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, "idle_timeout_in_minutes": { Type: schema.TypeInt, Optional: true, + Default: 4, ValidateFunc: validation.IntBetween(4, 30), }, @@ -117,6 +120,7 @@ func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error { tags := d.Get("tags").(map[string]interface{}) zones := expandZones(d.Get("zones").([]interface{})) + idleTimeout := d.Get("idle_timeout_in_minutes").(int) ipAllocationMethod := network.IPAllocationMethod(d.Get("public_ip_address_allocation").(string)) if strings.ToLower(string(sku.Name)) == "standard" { @@ -125,8 +129,16 @@ func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error { } } - properties := network.PublicIPAddressPropertiesFormat{ - PublicIPAllocationMethod: ipAllocationMethod, + publicIp := network.PublicIPAddress{ + Name: &name, + Location: &location, + Sku: &sku, + PublicIPAddressPropertiesFormat: &network.PublicIPAddressPropertiesFormat{ + PublicIPAllocationMethod: ipAllocationMethod, + IdleTimeoutInMinutes: utils.Int32(int32(idleTimeout)), + }, + Tags: expandTags(tags), + Zones: zones, } dnl, dnlOk := d.GetOk("domain_name_label") @@ -145,20 +157,7 @@ func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error { dnsSettings.DomainNameLabel = &domainNameLabel } - properties.DNSSettings = &dnsSettings - } - - if v, ok := d.GetOk("idle_timeout_in_minutes"); ok { - properties.IdleTimeoutInMinutes = utils.Int32(int32(v.(int))) - } - - publicIp := network.PublicIPAddress{ - Name: &name, - Location: &location, - Sku: &sku, - PublicIPAddressPropertiesFormat: &properties, - Tags: expandTags(tags), - Zones: zones, + publicIp.PublicIPAddressPropertiesFormat.DNSSettings = &dnsSettings } future, err := client.CreateOrUpdate(ctx, resGroup, name, publicIp) @@ -166,8 +165,7 @@ func resourceArmPublicIpCreate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error Creating/Updating Public IP %q (Resource Group %q): %+v", name, resGroup, err) } - err = future.WaitForCompletionRef(ctx, client.Client) - if err != nil { + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { return fmt.Errorf("Error waiting for completion of Public IP %q (Resource Group %q): %+v", name, resGroup, err) } @@ -222,20 +220,12 @@ func resourceArmPublicIpRead(d *schema.ResourceData, meta interface{}) error { d.Set("public_ip_address_allocation", strings.ToLower(string(props.PublicIPAllocationMethod))) if settings := props.DNSSettings; settings != nil { - if fqdn := settings.Fqdn; fqdn != nil { - d.Set("fqdn", fqdn) - } else { - d.Set("fqdn", "") - } - + d.Set("fqdn", settings.Fqdn) d.Set("domain_name_label", settings.DomainNameLabel) } - if ip := props.IPAddress; ip != nil { - d.Set("ip_address", ip) - } else { - d.Set("ip_address", "") - } + d.Set("ip_address", props.IPAddress) + d.Set("idle_timeout_in_minutes", props.IdleTimeoutInMinutes) } flattenAndSetTags(d, resp.Tags) @@ -259,8 +249,7 @@ func resourceArmPublicIpDelete(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("Error deleting Public IP %q (Resource Group %q): %+v", name, resGroup, err) } - err = future.WaitForCompletionRef(ctx, client.Client) - if err != nil { + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { return fmt.Errorf("Error waiting for deletion of Public IP %q (Resource Group %q): %+v", name, resGroup, err) } diff --git a/azurerm/resource_arm_public_ip_test.go b/azurerm/resource_arm_public_ip_test.go index 72bb578004a9..6fc922833be2 100644 --- a/azurerm/resource_arm_public_ip_test.go +++ b/azurerm/resource_arm_public_ip_test.go @@ -3,6 +3,8 @@ package azurerm import ( "fmt" "net/http" + "os" + "regexp" "testing" "github.com/hashicorp/terraform/helper/acctest" @@ -60,6 +62,11 @@ func TestAccAzureRMPublicIpStatic_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "public_ip_address_allocation", "static"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -84,11 +91,17 @@ func TestAccAzureRMPublicIpStatic_basic_withDNSLabel(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "domain_name_label", dnl), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } func TestAccAzureRMPublicIpStatic_standard(t *testing.T) { + resourceName := "azurerm_public_ip.test" ri := acctest.RandInt() config := testAccAzureRMPublicIPStatic_standard(ri, testLocation()) @@ -100,9 +113,14 @@ func TestAccAzureRMPublicIpStatic_standard(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - testCheckAzureRMPublicIpExists("azurerm_public_ip.test"), + testCheckAzureRMPublicIpExists(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -146,6 +164,11 @@ func TestAccAzureRMPublicIpStatic_idleTimeout(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "idle_timeout_in_minutes", "30"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -208,11 +231,17 @@ func TestAccAzureRMPublicIpStatic_update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "domain_name_label", fmt.Sprintf("acctest-%d", ri)), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } func TestAccAzureRMPublicIpDynamic_basic(t *testing.T) { + resourceName := "azurerm_public_ip.test" ri := acctest.RandInt() config := testAccAzureRMPublicIPDynamic_basic(ri, testLocation()) @@ -224,9 +253,38 @@ func TestAccAzureRMPublicIpDynamic_basic(t *testing.T) { { Config: config, Check: resource.ComposeTestCheckFunc( - testCheckAzureRMPublicIpExists("azurerm_public_ip.test"), + testCheckAzureRMPublicIpExists(resourceName), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMPublicIpStatic_importIdError(t *testing.T) { + resourceName := "azurerm_public_ip.test" + + ri := acctest.RandInt() + config := testAccAzureRMPublicIPStatic_basic(ri, testLocation()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMPublicIpDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateId: fmt.Sprintf("/subscriptions/%s/resourceGroups/acctestRG-%d/providers/Microsoft.Network/publicIPAdresses/acctestpublicip-%d", os.Getenv("ARM_SUBSCRIPTION_ID"), ri, ri), + ExpectError: regexp.MustCompile("Error parsing supplied resource id."), + }, }, }) } diff --git a/azurerm/resource_arm_servicebus_namespace.go b/azurerm/resource_arm_servicebus_namespace.go index 2decd619aab3..f53f4001c92f 100644 --- a/azurerm/resource_arm_servicebus_namespace.go +++ b/azurerm/resource_arm_servicebus_namespace.go @@ -3,13 +3,14 @@ package azurerm import ( "fmt" "log" + "regexp" "strings" "github.com/Azure/azure-sdk-for-go/services/servicebus/mgmt/2017-04-01/servicebus" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" - "regexp" ) // Default Authorization Rule/Policy created by Azure, used to populate the @@ -212,10 +213,18 @@ func resourceArmServiceBusNamespaceDelete(d *schema.ResourceData, meta interface resourceGroup := id.ResourceGroup name := id.Path["namespaces"] - _, err = client.Delete(ctx, resourceGroup, name) + future, err := client.Delete(ctx, resourceGroup, name) if err != nil { return err } + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting Service Bus %q: %+v", name, err) + } + return nil } diff --git a/azurerm/resource_arm_servicebus_namespace_test.go b/azurerm/resource_arm_servicebus_namespace_test.go index 3d681c79d96c..03fc38a5aa41 100644 --- a/azurerm/resource_arm_servicebus_namespace_test.go +++ b/azurerm/resource_arm_servicebus_namespace_test.go @@ -136,6 +136,26 @@ func TestAccAzureRMServiceBusNamespace_NonStandardCasing(t *testing.T) { }) } +func TestAccAzureRMServiceBusNamespace_premium(t *testing.T) { + resourceName := "azurerm_servicebus_namespace.test" + ri := acctest.RandInt() + config := testAccAzureRMServiceBusNamespace_premium(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusNamespaceDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusNamespaceExists(resourceName), + ), + }, + }, + }) +} + func testCheckAzureRMServiceBusNamespaceDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).serviceBusNamespacesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -221,3 +241,20 @@ resource "azurerm_servicebus_namespace" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMServiceBusNamespace_premium(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Premium" + capacity = 1 +} +`, rInt, location, rInt) +} diff --git a/azurerm/resource_arm_servicebus_queue.go b/azurerm/resource_arm_servicebus_queue.go index dce867969e77..5d2c3ac96cfb 100644 --- a/azurerm/resource_arm_servicebus_queue.go +++ b/azurerm/resource_arm_servicebus_queue.go @@ -40,21 +40,24 @@ func resourceArmServiceBusQueue() *schema.Resource { "resource_group_name": resourceGroupNameSchema(), "auto_delete_on_idle": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "default_message_ttl": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "duplicate_detection_history_time_window": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "enable_express": { diff --git a/azurerm/resource_arm_servicebus_queue_test.go b/azurerm/resource_arm_servicebus_queue_test.go index 11e5c2a53b0c..b50fdeadaabe 100644 --- a/azurerm/resource_arm_servicebus_queue_test.go +++ b/azurerm/resource_arm_servicebus_queue_test.go @@ -230,6 +230,30 @@ func TestAccAzureRMServiceBusQueue_lockDuration(t *testing.T) { }) } +func TestAccAzureRMServiceBusQueue_isoTimeSpanAttributes(t *testing.T) { + resourceName := "azurerm_servicebus_queue.test" + ri := acctest.RandInt() + config := testAccAzureRMServiceBusQueue_isoTimeSpanAttributes(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusQueueDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusQueueExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_delete_on_idle", "PT10M"), + resource.TestCheckResourceAttr(resourceName, "default_message_ttl", "PT30M"), + resource.TestCheckResourceAttr(resourceName, "requires_duplicate_detection", "true"), + resource.TestCheckResourceAttr(resourceName, "duplicate_detection_history_time_window", "PT15M"), + ), + }, + }, + }) +} + func testCheckAzureRMServiceBusQueueDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).serviceBusQueuesClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -501,3 +525,29 @@ resource "azurerm_servicebus_queue" "test" { } `, rInt, location, rInt, rInt) } + +func testAccAzureRMServiceBusQueue_isoTimeSpanAttributes(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + sku = "standard" +} + +resource "azurerm_servicebus_queue" "test" { + name = "acctestservicebusqueue-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + auto_delete_on_idle = "PT10M" + default_message_ttl = "PT30M" + requires_duplicate_detection = true + duplicate_detection_history_time_window = "PT15M" +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_servicebus_topic.go b/azurerm/resource_arm_servicebus_topic.go index dca3941bcd12..64d5dd85fe84 100644 --- a/azurerm/resource_arm_servicebus_topic.go +++ b/azurerm/resource_arm_servicebus_topic.go @@ -53,21 +53,24 @@ func resourceArmServiceBusTopic() *schema.Resource { }, "auto_delete_on_idle": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "default_message_ttl": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "duplicate_detection_history_time_window": { - Type: schema.TypeString, - Optional: true, - Computed: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateIso8601Duration(), }, "enable_batched_operations": { diff --git a/azurerm/resource_arm_servicebus_topic_test.go b/azurerm/resource_arm_servicebus_topic_test.go index 33146c282ccf..8842af0c7082 100644 --- a/azurerm/resource_arm_servicebus_topic_test.go +++ b/azurerm/resource_arm_servicebus_topic_test.go @@ -201,6 +201,30 @@ func TestAccAzureRMServiceBusTopic_enableDuplicateDetection(t *testing.T) { }) } +func TestAccAzureRMServiceBusTopic_isoTimeSpanAttributes(t *testing.T) { + resourceName := "azurerm_servicebus_topic.test" + ri := acctest.RandInt() + config := testAccAzureRMServiceBusTopic_isoTimeSpanAttributes(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMServiceBusTopicDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMServiceBusTopicExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_delete_on_idle", "PT10M"), + resource.TestCheckResourceAttr(resourceName, "default_message_ttl", "PT30M"), + resource.TestCheckResourceAttr(resourceName, "requires_duplicate_detection", "true"), + resource.TestCheckResourceAttr(resourceName, "duplicate_detection_history_time_window", "PT15M"), + ), + }, + }, + }) +} + func testCheckAzureRMServiceBusTopicDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).serviceBusTopicsClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -425,3 +449,29 @@ resource "azurerm_servicebus_topic" "test" { } `, rInt, location, rInt, rInt) } + +func testAccAzureRMServiceBusTopic_isoTimeSpanAttributes(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + namespace_name = "${azurerm_servicebus_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + auto_delete_on_idle = "PT10M" + default_message_ttl = "PT30M" + requires_duplicate_detection = true + duplicate_detection_history_time_window = "PT15M" +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 66989c91ca63..88adab6971be 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -213,8 +213,9 @@ func resourceArmVirtualMachine() *schema.Resource { Computed: true, ConflictsWith: []string{"storage_os_disk.0.vhd_uri"}, ValidateFunc: validation.StringInSlice([]string{ - string(compute.PremiumLRS), - string(compute.StandardLRS), + string(compute.StorageAccountTypesPremiumLRS), + string(compute.StorageAccountTypesStandardLRS), + string(compute.StorageAccountTypesStandardSSDLRS), }, true), }, @@ -285,8 +286,9 @@ func resourceArmVirtualMachine() *schema.Resource { Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{ - string(compute.PremiumLRS), - string(compute.StandardLRS), + string(compute.StorageAccountTypesPremiumLRS), + string(compute.StorageAccountTypesStandardLRS), + string(compute.StorageAccountTypesStandardSSDLRS), }, true), }, diff --git a/azurerm/resource_arm_virtual_machine_extension.go b/azurerm/resource_arm_virtual_machine_extension.go index 1314da33cd0b..232b999fa24d 100644 --- a/azurerm/resource_arm_virtual_machine_extension.go +++ b/azurerm/resource_arm_virtual_machine_extension.go @@ -188,9 +188,6 @@ func resourceArmVirtualMachineExtensionsRead(d *schema.ResourceData, meta interf } } - if resp.VirtualMachineExtensionProperties.Settings != nil { - } - flattenAndSetTags(d, resp.Tags) return nil diff --git a/azurerm/resource_arm_virtual_machine_managed_disks_test.go b/azurerm/resource_arm_virtual_machine_managed_disks_test.go index dbf2d6daf3a3..b300df8b3931 100644 --- a/azurerm/resource_arm_virtual_machine_managed_disks_test.go +++ b/azurerm/resource_arm_virtual_machine_managed_disks_test.go @@ -16,6 +16,27 @@ import ( // NOTE: Test `TestAccAzureRMVirtualMachine_enableAnWithVM` requires a machine of size `D8_v3` which is large/expensive - you may wish to ignore this test" +func TestAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_standardSSD(t *testing.T) { + resourceName := "azurerm_virtual_machine.test" + var vm compute.VirtualMachine + ri := acctest.RandInt() + config := testAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_standardSSD(ri, testLocation()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineExists(resourceName, &vm), + resource.TestCheckResourceAttr(resourceName, "storage_os_disk.0.managed_disk_type", "StandardSSD_LRS"), + ), + }, + }, + }) +} + func TestAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_explicit(t *testing.T) { var vm compute.VirtualMachine ri := acctest.RandInt() @@ -1040,6 +1061,79 @@ resource "azurerm_virtual_machine" "test" { `, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) } +func testAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_standardSSD(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_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 = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osd-%d" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "50" + managed_disk_type = "StandardSSD_LRS" + } + + os_profile { + computer_name = "hn%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + tags { + environment = "Production" + cost-center = "Ops" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) +} + func testAccAzureRMVirtualMachine_basicLinuxMachine_managedDisk_implicit(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_virtual_machine_scale_set.go b/azurerm/resource_arm_virtual_machine_scale_set.go index 17106a18ef6c..bcd153f3226b 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set.go +++ b/azurerm/resource_arm_virtual_machine_scale_set.go @@ -479,8 +479,9 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource { Computed: true, ConflictsWith: []string{"storage_profile_os_disk.vhd_containers"}, ValidateFunc: validation.StringInSlice([]string{ - string(compute.PremiumLRS), - string(compute.StandardLRS), + string(compute.StorageAccountTypesPremiumLRS), + string(compute.StorageAccountTypesStandardLRS), + string(compute.StorageAccountTypesStandardSSDLRS), }, true), }, @@ -537,8 +538,9 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource { Optional: true, Computed: true, ValidateFunc: validation.StringInSlice([]string{ - string(compute.PremiumLRS), - string(compute.StandardLRS), + string(compute.StorageAccountTypesPremiumLRS), + string(compute.StorageAccountTypesStandardLRS), + string(compute.StorageAccountTypesStandardSSDLRS), }, true), }, }, diff --git a/azurerm/resource_arm_virtual_machine_scale_set_test.go b/azurerm/resource_arm_virtual_machine_scale_set_test.go index c53f6e6973e1..ef5092302b93 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set_test.go +++ b/azurerm/resource_arm_virtual_machine_scale_set_test.go @@ -39,6 +39,31 @@ func TestAccAzureRMVirtualMachineScaleSet_basic(t *testing.T) { }) } +func TestAccAzureRMVirtualMachineScaleSet_standardSSD(t *testing.T) { + resourceName := "azurerm_virtual_machine_scale_set.test" + ri := acctest.RandInt() + config := testAccAzureRMVirtualMachineScaleSet_standardSSD(ri, testLocation()) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineScaleSetExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"os_profile.0.admin_password"}, + }, + }, + }) +} + func TestAccAzureRMVirtualMachineScaleSet_basicPublicIP(t *testing.T) { resourceName := "azurerm_virtual_machine_scale_set.test" ri := acctest.RandInt() @@ -1160,6 +1185,91 @@ resource "azurerm_virtual_machine_scale_set" "test" { `, rInt, location) } +func testAccAzureRMVirtualMachineScaleSet_standardSSD(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]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-%[1]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_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +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_scale_set" "test" { + name = "acctvmss-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + single_placement_group = false + + sku { + name = "Standard_D1_v2" + tier = "Standard" + capacity = 2 + } + + os_profile { + computer_name_prefix = "testvm-%[1]d" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + network_profile { + name = "TestNetworkProfile-%[1]d" + primary = true + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + } + } + + storage_profile_os_disk { + name = "" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "StandardSSD_LRS" + } + + storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, rInt, location) +} + func testAccAzureRMVirtualMachineScaleSet_basicPublicIP(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_virtual_network_gateway_connection.go b/azurerm/resource_arm_virtual_network_gateway_connection.go index 1d2e92b449b0..663ab380d3a4 100644 --- a/azurerm/resource_arm_virtual_network_gateway_connection.go +++ b/azurerm/resource_arm_virtual_network_gateway_connection.go @@ -7,6 +7,8 @@ import ( "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-04-01/network" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -16,15 +18,17 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { Read: resourceArmVirtualNetworkGatewayConnectionRead, Update: resourceArmVirtualNetworkGatewayConnectionCreateUpdate, Delete: resourceArmVirtualNetworkGatewayConnectionDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, }, "resource_group_name": resourceGroupNameSchema(), @@ -40,36 +44,41 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.IPsec), string(network.Vnet2Vnet), }, true), - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, }, "virtual_network_gateway_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, }, "authorization_key": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.NoZeroValues, }, "express_route_circuit_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceIDOrEmpty, }, "peer_virtual_network_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceIDOrEmpty, }, "local_network_gateway_id": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceIDOrEmpty, }, "enable_bgp": { @@ -85,9 +94,10 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { }, "routing_weight": { - Type: schema.TypeInt, - Optional: true, - Computed: true, + Type: schema.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 1000), }, "shared_key": { @@ -105,7 +115,7 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { "dh_group": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.DHGroup1), string(network.DHGroup14), @@ -117,10 +127,11 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.None), }, true), }, + "ike_encryption": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.AES128), string(network.AES192), @@ -129,10 +140,11 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.DES3), }, true), }, + "ike_integrity": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.IkeIntegrityGCMAES128), string(network.IkeIntegrityGCMAES256), @@ -142,10 +154,11 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.IkeIntegritySHA384), }, true), }, + "ipsec_encryption": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.IpsecEncryptionAES128), string(network.IpsecEncryptionAES192), @@ -158,10 +171,11 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.IpsecEncryptionNone), }, true), }, + "ipsec_integrity": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.IpsecIntegrityGCMAES128), string(network.IpsecIntegrityGCMAES192), @@ -171,10 +185,11 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.IpsecIntegritySHA256), }, true), }, + "pfs_group": { Type: schema.TypeString, Required: true, - DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ string(network.PfsGroupECP256), string(network.PfsGroupECP384), @@ -185,12 +200,14 @@ func resourceArmVirtualNetworkGatewayConnection() *schema.Resource { string(network.PfsGroupPFS24), }, true), }, + "sa_datasize": { Type: schema.TypeInt, Optional: true, Computed: true, ValidateFunc: validation.IntAtLeast(1024), }, + "sa_lifetime": { Type: schema.TypeInt, Optional: true, @@ -234,8 +251,7 @@ func resourceArmVirtualNetworkGatewayConnectionCreateUpdate(d *schema.ResourceDa return fmt.Errorf("Error Creating/Updating AzureRM Virtual Network Gateway Connection %q (Resource Group %q): %+v", name, resGroup, err) } - err = future.WaitForCompletionRef(ctx, client.Client) - if err != nil { + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { return fmt.Errorf("Error waiting for completion of Virtual Network Gateway Connection %q (Resource Group %q): %+v", name, resGroup, err) } @@ -345,8 +361,7 @@ func resourceArmVirtualNetworkGatewayConnectionDelete(d *schema.ResourceData, me return fmt.Errorf("Error Deleting Virtual Network Gateway Connection %q (Resource Group %q): %+v", name, resGroup, err) } - err = future.WaitForCompletionRef(ctx, client.Client) - if err != nil { + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { return fmt.Errorf("Error waiting for deletion of Virtual Network Gateway Connection %q (Resource Group %q): %+v", name, resGroup, err) } @@ -357,11 +372,14 @@ func getArmVirtualNetworkGatewayConnectionProperties(d *schema.ResourceData) (*n connectionType := network.VirtualNetworkGatewayConnectionType(d.Get("type").(string)) props := &network.VirtualNetworkGatewayConnectionPropertiesFormat{ - ConnectionType: connectionType, + ConnectionType: connectionType, + EnableBgp: utils.Bool(d.Get("enable_bgp").(bool)), + UsePolicyBasedTrafficSelectors: utils.Bool(d.Get("use_policy_based_traffic_selectors").(bool)), } if v, ok := d.GetOk("virtual_network_gateway_id"); ok { virtualNetworkGatewayId := v.(string) + _, name, err := resourceGroupAndVirtualNetworkGatewayFromId(virtualNetworkGatewayId) if err != nil { return nil, fmt.Errorf("Error Getting VirtualNetworkGateway Name and Group:: %+v", err) @@ -420,10 +438,6 @@ func getArmVirtualNetworkGatewayConnectionProperties(d *schema.ResourceData) (*n } } - props.EnableBgp = utils.Bool(d.Get("enable_bgp").(bool)) - - props.UsePolicyBasedTrafficSelectors = utils.Bool(d.Get("use_policy_based_traffic_selectors").(bool)) - if v, ok := d.GetOk("routing_weight"); ok { routingWeight := int32(v.(int)) props.RoutingWeight = &routingWeight @@ -434,8 +448,7 @@ func getArmVirtualNetworkGatewayConnectionProperties(d *schema.ResourceData) (*n } if v, ok := d.GetOk("ipsec_policy"); ok { - ipsecPolicies := v.([]interface{}) - props.IpsecPolicies = expandArmVirtualNetworkGatewayConnectionIpsecPolicies(ipsecPolicies) + props.IpsecPolicies = expandArmVirtualNetworkGatewayConnectionIpsecPolicies(v.([]interface{})) } if props.ConnectionType == network.ExpressRoute { diff --git a/azurerm/resource_arm_virtual_network_gateway_connection_test.go b/azurerm/resource_arm_virtual_network_gateway_connection_test.go index 9d9fc8bb0a2f..d58ac0307e19 100644 --- a/azurerm/resource_arm_virtual_network_gateway_connection_test.go +++ b/azurerm/resource_arm_virtual_network_gateway_connection_test.go @@ -11,6 +11,7 @@ import ( ) func TestAccAzureRMVirtualNetworkGatewayConnection_sitetosite(t *testing.T) { + resourceName := "azurerm_virtual_network_gateway_connection.test" ri := acctest.RandInt() config := testAccAzureRMVirtualNetworkGatewayConnection_sitetosite(ri, testLocation()) @@ -25,6 +26,11 @@ func TestAccAzureRMVirtualNetworkGatewayConnection_sitetosite(t *testing.T) { testCheckAzureRMVirtualNetworkGatewayConnectionExists("azurerm_virtual_network_gateway_connection.test"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } diff --git a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go index 7d8c177ade4e..9dd7a1d27c5f 100644 --- a/vendor/github.com/Azure/go-autorest/autorest/azure/async.go +++ b/vendor/github.com/Azure/go-autorest/autorest/azure/async.go @@ -428,6 +428,7 @@ func (pt *pollingTrackerBase) pollForStatus(sender autorest.Sender) error { } else { // check response body for error content pt.updateErrorFromResponse() + err = pt.pollingError() } return err } diff --git a/vendor/github.com/Azure/go-autorest/version/version.go b/vendor/github.com/Azure/go-autorest/version/version.go index a85b1213c1d2..ad2d6099f52b 100644 --- a/vendor/github.com/Azure/go-autorest/version/version.go +++ b/vendor/github.com/Azure/go-autorest/version/version.go @@ -20,7 +20,7 @@ import ( ) // Number contains the semantic version of this SDK. -const Number = "v10.15.3" +const Number = "v10.15.4" var ( userAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", diff --git a/vendor/vendor.json b/vendor/vendor.json index d28bfca04a70..891fc7f11c8c 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -373,74 +373,74 @@ { "checksumSHA1": "1Y2+bSzYrdPHQqRjR1OrBMHAvxY=", "path": "github.com/Azure/go-autorest/autorest", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "GxL0HHpZDj2milPhR3SPV6MWLPc=", "path": "github.com/Azure/go-autorest/autorest/adal", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { - "checksumSHA1": "GtY6Y3+F3862tfRZJ3r1FC+/tRE=", + "checksumSHA1": "iabndyKiv9sLDixJh4izKt7Nniw=", "path": "github.com/Azure/go-autorest/autorest/azure", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "Fkezj7ch01wYNvXzQZdhmeSf/mk=", "path": "github.com/Azure/go-autorest/autorest/azure/cli", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "9nXCi9qQsYjxCeajJKWttxgEt0I=", "path": "github.com/Azure/go-autorest/autorest/date", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "SbBb2GcJNm5GjuPKGL2777QywR4=", "path": "github.com/Azure/go-autorest/autorest/to", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "HjdLfAF3oA2In8F3FKh/Y+BPyXk=", "path": "github.com/Azure/go-autorest/autorest/validation", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "b2lrPJRxf+MEfmMafN40wepi5WM=", "path": "github.com/Azure/go-autorest/logger", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { - "checksumSHA1": "scpSozMdk4sqSpkbQqupLKUfLiM=", + "checksumSHA1": "JVAdK/V4SFCsCdpRBd4hTq/Z3h4=", "path": "github.com/Azure/go-autorest/version", - "revision": "a35eae345f69bbfbe3b8fa0b1d3fe98f8430b21a", - "revisionTime": "2018-08-30T19:44:05Z", - "version": "v10.15.3", - "versionExact": "v10.15.3" + "revision": "a88c19ef2016e095f0b6c3b451074b4663f53bed", + "revisionTime": "2018-09-11T16:08:53Z", + "version": "v10.15.4", + "versionExact": "v10.15.4" }, { "checksumSHA1": "jQh1fnoKPKMURvKkpdRjN695nAQ=", diff --git a/website/azurerm.erb b/website/azurerm.erb index 339fa4c1a12f..de3fa211570f 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -417,6 +417,10 @@ azurerm_mysql_server + > + azurerm_mysql_virtual_network_rule + + > azurerm_postgresql_configuration @@ -751,6 +755,14 @@ azurerm_express_route_circuit_peering + > + azurerm_firewall + + + > + azurerm_firewall_network_rule_collection + + > azurerm_local_network_gateway diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index 747d756d3d7a..2dc91afa5a2f 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -131,7 +131,7 @@ resource "azurerm_application_gateway" "network" { url_path_map { name = "pbr.contoso.com" default_backend_address_pool_name = "${azurerm_virtual_network.vnet.name}-beap-fallback" - default_backend_http_settings_name = ${azurerm_virtual_network.vnet.name}-be-htst" + default_backend_http_settings_name = "${azurerm_virtual_network.vnet.name}-be-htst" path_rule { name = "pbr.contoso.com_first" diff --git a/website/docs/r/firewall.html.markdown b/website/docs/r/firewall.html.markdown new file mode 100644 index 000000000000..b4a12f991d0d --- /dev/null +++ b/website/docs/r/firewall.html.markdown @@ -0,0 +1,107 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_firewall" +sidebar_current: "docs-azurerm-resource-network-firewall-x" +description: |- + Manages an Azure Firewall. + +--- + +# azurerm_firewall + +Manages an Azure Firewall. + +-> **NOTE** Azure Firewall is currently in Public Preview. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "example-resources" + location = "North Europe" +} + +resource "azurerm_virtual_network" "test" { + name = "testvnet" + 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 = "AzureFirewallSubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "testpip" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "test" { + name = "testfirewall" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "configuration" + subnet_id = "${azurerm_subnet.test.id}" + internal_public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Firewall. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to create the resource. Changing this forces a new resource to be created. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `ip_configuration` - (Required) A `ip_configuration` block as documented below. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +A `ip_configuration` block supports the following: + +* `name` - (Required) Specifies the name of the IP Configuration. + +* `subnet_id` - (Required) Reference to the subnet associated with the IP Configuration. + +-> **NOTE** The Subnet used for the Firewall must have the name `AzureFirewallSubnet` and the subnet mask must be at least `/25`. + +* `internal_public_ip_address_id` - (Required) The Resource ID of the Public IP Address associated with the firewall. + +-> **NOTE** The Public IP must have a `Static` allocation and `Standard` sku. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Resource ID of the Azure Firewall. + +* `ip_configuration` - A `ip_configuration` block as defined below. + +--- + +A `ip_configuration` block exports the following: + +* `private_ip_address` - The private IP address of the Azure Firewall. + +## Import + +Azure Firewalls can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_firewall.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Network/azureFirewalls/testfirewall +``` diff --git a/website/docs/r/firewall_network_rule_collection.html.markdown b/website/docs/r/firewall_network_rule_collection.html.markdown new file mode 100644 index 000000000000..0981c32b7c34 --- /dev/null +++ b/website/docs/r/firewall_network_rule_collection.html.markdown @@ -0,0 +1,127 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_firewall_network_rule_collection" +sidebar_current: "docs-azurerm-resource-network-firewall-network-rule-collection" +description: |- + Manages a Network Rule Collection within an Azure Firewall. + +--- + +# azurerm_firewall_network_rule_collection + +Manages a Network Rule Collection within an Azure Firewall. + +-> **NOTE** Azure Firewall is currently in Public Preview. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "example-resources" + location = "North Europe" +} + +resource "azurerm_virtual_network" "test" { + name = "testvnet" + 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 = "AzureFirewallSubnet" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.1.0/24" +} + +resource "azurerm_public_ip" "test" { + name = "testpip" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + public_ip_address_allocation = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "test" { + name = "testfirewall" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "configuration" + subnet_id = "${azurerm_subnet.test.id}" + internal_public_ip_address_id = "${azurerm_public_ip.test.id}" + } +} + +resource "azurerm_firewall_network_rule_collection" "test" { + name = "testcollection" + azure_firewall_name = "${azurerm_firewall.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + priority = 100 + action = "Allow" + + rule { + name = "testrule" + + source_addresses = [ + "10.0.0.0/16", + ] + + destination_ports = [ + "53", + ] + + destination_addresses = [ + "8.8.8.8", + "8.8.4.4", + ] + + protocols = [ + "TCP", + "UDP", + ] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Network Rule Collection which must be unique within the Firewall. Changing this forces a new resource to be created. + +* `azure_firewall_name` - (Required) Specifies the name of the Firewall in which to the Network Rule Collection should be created. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) Specifies the name of the Resource Group in which the Firewall exists. Changing this forces a new resource to be created. + +* `priority` - (Required) Specifies the priority of the rule collection. Possible values are between `100` - `65000`. + +* `action` - (Required) Specifies the action the rule will apply to matching traffic. Possible values are `Allow` and `Deny`. + +* `rule` - (Required) One or more `rule` blocks as defined below. + +--- + +A `rule` block supports the following: + +* `name` - (Required) Specifies the name of the rule. + +* `description` - (Optional) Specifies a description for the rule. + +* `source_addresses` - (Required) A list of source IP addresses and/or IP ranges. + +* `destination_addresses` - (Required) A list of destination IP addresses and/or IP ranges. + +* `destination_ports` - (Required) A list of destination ports. + +* `protocols` - (Required) A list of protocols. Possible values are `Any`, `ICMP`, `TCP` and `UDP`. + +## Import + +Azure Firewall Network Rule Collection's can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_firewall_network_rule_collection.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/azureFirewalls/myfirewall/networkRuleCollections/mycollection +``` diff --git a/website/docs/r/key_vault_certificate.html.markdown b/website/docs/r/key_vault_certificate.html.markdown index b45953bd9f1f..575a44d0252c 100644 --- a/website/docs/r/key_vault_certificate.html.markdown +++ b/website/docs/r/key_vault_certificate.html.markdown @@ -251,7 +251,8 @@ The following attributes are exported: * `id` - The Key Vault Certificate ID. * `secret_id` - The ID of the associated Key Vault Secret. * `version` - The current version of the Key Vault Certificate. -* `certificate_data` - The raw Key Vault Certificate +* `certificate_data` - The raw Key Vault Certificate. +* `thumbprint` - The X509 Thumbprint of the Key Vault Certificate returned as hex string. ## Import diff --git a/website/docs/r/mysql_virtual_network_rule.html.markdown b/website/docs/r/mysql_virtual_network_rule.html.markdown new file mode 100644 index 000000000000..f1d6ea24e48b --- /dev/null +++ b/website/docs/r/mysql_virtual_network_rule.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_mysql_virtual_network_rule" +sidebar_current: "docs-azurerm-resource-database-mysql-virtual-network-rule" +description: |- + Manages a MySQL Virtual Network Rule. +--- + +# azurerm_mysql_virtual_network_rule + +Manages a MySQL Virtual Network Rule. + +-> **NOTE:** MySQL Virtual Network Rules [can only be used with SKU Tiers of `GeneralPurpose` or `MemoryOptimized`](https://docs.microsoft.com/en-us/azure/mysql/concepts-data-access-and-security-vnet) + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_virtual_network" "test" { + name = "example-vnet" + address_space = ["10.7.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "internal" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "mysql-server-1" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "mysqladminun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.7" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 5120 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "mysql-vnet-rule" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.internal.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the MySQL Virtual Network Rule. Cannot be empty and must only contain alphanumeric characters and hyphens. Cannot start with a number, and cannot start or end with a hyphen. Changing this forces a new resource to be created. + +~> **NOTE:** `name` must be between 1-128 characters long and must satisfy all of the requirements below: +1. Contains only alphanumeric and hyphen characters +2. Cannot start with a number or hyphen +3. Cannot end with a hyphen + +* `resource_group_name` - (Required) The name of the resource group where the MySQL server resides. Changing this forces a new resource to be created. + +* `server_name` - (Required) The name of the SQL Server to which this MySQL virtual network rule will be applied to. Changing this forces a new resource to be created. + +* `subnet_id` - (Required) The ID of the subnet that the MySQL server will be connected to. + +~> **NOTE:** Due to [a bug in the Azure API](https://github.com/Azure/azure-rest-api-specs/issues/3719) this resource currently doesn't expose the `ignore_missing_vnet_service_endpoint` field and defaults this to `false`. Terraform will check during the provisioning of the Virtual Network Rule that the Subnet contains the Service Rule to verify that the Virtual Network Rule can be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the MySQL Virtual Network Rule. + +## Import + +MySQL Virtual Network Rules can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_mysql_virtual_network_rule.rule1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.DBforMySQL/servers/myserver/virtualNetworkRules/vnetrulename +``` diff --git a/website/docs/r/postgresql_firewall_rule.html.markdown b/website/docs/r/postgresql_firewall_rule.html.markdown index 8fd7553f2a0a..eabdbc846812 100644 --- a/website/docs/r/postgresql_firewall_rule.html.markdown +++ b/website/docs/r/postgresql_firewall_rule.html.markdown @@ -63,7 +63,7 @@ The following arguments are supported: * `resource_group_name` - (Required) The name of the resource group in which the PostgreSQL Server exists. Changing this forces a new resource to be created. -* `start_ip_address` - (Required) Specifies the Charset for the PostgreSQL Database. Changing this forces a new resource to be created. +* `start_ip_address` - (Required) Specifies the Start IP Address associated with this Firewall Rule. Changing this forces a new resource to be created. * `end_ip_address` - (Required) Specifies the End IP Address associated with this Firewall Rule. Changing this forces a new resource to be created. diff --git a/website/docs/r/postgresql_virtual_network_rule.html.markdown b/website/docs/r/postgresql_virtual_network_rule.html.markdown index 79de06acdd9b..026e4f7af8d2 100644 --- a/website/docs/r/postgresql_virtual_network_rule.html.markdown +++ b/website/docs/r/postgresql_virtual_network_rule.html.markdown @@ -10,6 +10,8 @@ description: |- Manages a PostgreSQL Virtual Network Rule. +-> **NOTE:** PostgreSQL Virtual Network Rules [can only be used with SKU Tiers of `GeneralPurpose` or `MemoryOptimized`](https://docs.microsoft.com/en-us/azure/postgresql/concepts-data-access-and-security-vnet) + ## Example Usage ```hcl @@ -39,16 +41,16 @@ resource "azurerm_postgresql_server" "test" { resource_group_name = "${azurerm_resource_group.test.name}" sku { - name = "B_Gen4_2" + name = "GP_Gen5_2" capacity = 2 - tier = "Basic" - family = "Gen4" + tier = "GeneralPurpose" + family = "Gen5" } storage_profile { - storage_mb = 5120 + storage_mb = 5120 backup_retention_days = 7 - geo_redundant_backup = "Disabled" + geo_redundant_backup = "Disabled" } administrator_login = "psqladminun" diff --git a/website/docs/r/servicebus_queue.html.markdown b/website/docs/r/servicebus_queue.html.markdown index 0127b37b7259..28bff27eb3ca 100644 --- a/website/docs/r/servicebus_queue.html.markdown +++ b/website/docs/r/servicebus_queue.html.markdown @@ -63,16 +63,14 @@ The following arguments are supported: * `resource_group_name` - (Required) The name of the resource group in which to create the namespace. Changing this forces a new resource to be created. -* `auto_delete_on_idle` - (Optional) The idle interval after which the - Queue is automatically deleted, minimum of 5 minutes. Provided in the [TimeSpan](#timespan-format) - format. +* `auto_delete_on_idle` - (Optional) The ISO 8601 timespan duration of the idle interval after which the + Queue is automatically deleted, minimum of 5 minutes. -* `default_message_ttl` - (Optional) The TTL of messages sent to this queue. This is the default value - used when TTL is not set on message itself. Provided in the [TimeSpan](#timespan-format) - format. +* `default_message_ttl` - (Optional) The ISO 8601 timespan duration of the TTL of messages sent to this + queue. This is the default value used when TTL is not set on message itself. -* `duplicate_detection_history_time_window` - (Optional) The duration during which - duplicates can be detected. Default value is 10 minutes. Provided in the [TimeSpan](#timespan-format) format. +* `duplicate_detection_history_time_window` - (Optional) The ISO 8601 timespan duration during which + duplicates can be detected. Default value is 10 minutes. (`PT10M`) * `enable_express` - (Optional) Boolean flag which controls whether Express Entities are enabled. An express queue holds a message in memory temporarily before writing @@ -104,11 +102,6 @@ The following arguments are supported: Changing this forces a new resource to be created. Defaults to `false`. * `dead_lettering_on_message_expiration` - (Optional) Boolean flag which controls whether the Queue has dead letter support when a message expires. Defaults to `false`. - -### TimeSpan Format - -Some arguments for this resource are required in the TimeSpan format which is -used to represent a length of time. The supported format is documented [here](https://msdn.microsoft.com/en-us/library/se73z7b9(v=vs.110).aspx#Anchor_2) ## Attributes Reference diff --git a/website/docs/r/servicebus_topic.html.markdown b/website/docs/r/servicebus_topic.html.markdown index f2ce29ba6b27..8a89ea25425d 100644 --- a/website/docs/r/servicebus_topic.html.markdown +++ b/website/docs/r/servicebus_topic.html.markdown @@ -63,16 +63,14 @@ The following arguments are supported: * `status` - (Optional) The Status of the Service Bus Topic. Acceptable values are `Active` or `Disabled`. Defaults to `Active`. -* `auto_delete_on_idle` - (Optional) The idle interval after which the - Topic is automatically deleted, minimum of 5 minutes. Provided in the [TimeSpan](#timespan-format) - format. +* `auto_delete_on_idle` - (Optional) The ISO 8601 timespan duration of the idle interval after which the + Topic is automatically deleted, minimum of 5 minutes. -* `default_message_ttl` - (Optional) The TTL of messages sent to this topic if no - TTL value is set on the message itself. Provided in the [TimeSpan](#timespan-format) - format. +* `default_message_ttl` - (Optional) The ISO 8601 timespan duration of TTL of messages sent to this topic if no + TTL value is set on the message itself. -* `duplicate_detection_history_time_window` - (Optional) The duration during which - duplicates can be detected. Provided in the [TimeSpan](#timespan-format) format. Defaults to 10 minutes (`00:10:00`) +* `duplicate_detection_history_time_window` - (Optional) The ISO 8601 timespan duration during which + duplicates can be detected. Defaults to 10 minutes. (`PT10M`) * `enable_batched_operations` - (Optional) Boolean flag which controls if server-side batched operations are enabled. Defaults to false. @@ -98,11 +96,6 @@ The following arguments are supported: * `support_ordering` - (Optional) Boolean flag which controls whether the Topic supports ordering. Defaults to false. -### TimeSpan Format - -Some arguments for this resource are required in the TimeSpan format which is -used to represent a lengh of time. The supported format is documented [here](https://msdn.microsoft.com/en-us/library/se73z7b9(v=vs.110).aspx#Anchor_2) - ## Attributes Reference The following attributes are exported: diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index 838843edf1b3..1cc91d80126d 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -302,7 +302,7 @@ A `storage_data_disk` block supports the following: The following properties apply when using Managed Disks: -* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Possible values are either `Standard_LRS` or `Premium_LRS`. +* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Possible values are either `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS`. * `managed_disk_id` - (Optional) Specifies the ID of an Existing Managed Disk which should be attached to this Virtual Machine. When this field is set `create_option` must be set to `Attach`. @@ -332,7 +332,7 @@ The following properties apply when using Managed Disks: * `managed_disk_id` - (Optional) Specifies the ID of an existing Managed Disk which should be attached as the OS Disk of this Virtual Machine. If this is set then the `create_option` must be set to `Attach`. -* `managed_disk_type` - (Optional) Specifies the type of Managed Disk which should be created. Possible values are `Standard_LRS` or `Premium_LRS`. +* `managed_disk_type` - (Optional) Specifies the type of Managed Disk which should be created. Possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS`. The following properties apply when using Unmanaged Disks: diff --git a/website/docs/r/virtual_machine_scale_set.html.markdown b/website/docs/r/virtual_machine_scale_set.html.markdown index b656e4011f26..02c189f56d2e 100644 --- a/website/docs/r/virtual_machine_scale_set.html.markdown +++ b/website/docs/r/virtual_machine_scale_set.html.markdown @@ -388,7 +388,7 @@ output "principal_id" { * `name` - (Optional) Specifies the disk name. Must be specified when using unmanaged disk ('managed_disk_type' property not set). * `vhd_containers` - (Optional) Specifies the vhd uri. Cannot be used when `image` or `managed_disk_type` is specified. -* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Value you must be either `Standard_LRS` or `Premium_LRS`. Cannot be used when `vhd_containers` or `image` is specified. +* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Value you must be either `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS`. Cannot be used when `vhd_containers` or `image` is specified. * `create_option` - (Required) Specifies how the virtual machine should be created. The only possible option is `FromImage`. * `caching` - (Optional) Specifies the caching requirements. Possible values include: `None` (default), `ReadOnly`, `ReadWrite`. * `image` - (Optional) Specifies the blob uri for user image. A virtual machine scale set creates an os disk in the same container as the user image. @@ -402,7 +402,7 @@ output "principal_id" { * `create_option` - (Optional) Specifies how the data disk should be created. The only possible options are `FromImage` and `Empty`. * `caching` - (Optional) Specifies the caching requirements. Possible values include: `None` (default), `ReadOnly`, `ReadWrite`. * `disk_size_gb` - (Optional) Specifies the size of the disk in GB. This element is required when creating an empty disk. -* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Value must be either `Standard_LRS` or `Premium_LRS`. +* `managed_disk_type` - (Optional) Specifies the type of managed disk to create. Value must be either `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS`. `storage_profile_image_reference` supports the following: diff --git a/website/docs/r/virtual_network_gateway_connection.html.markdown b/website/docs/r/virtual_network_gateway_connection.html.markdown index 0407a8c0de91..bea46f86c06c 100644 --- a/website/docs/r/virtual_network_gateway_connection.html.markdown +++ b/website/docs/r/virtual_network_gateway_connection.html.markdown @@ -19,66 +19,66 @@ and an on-premises VPN device and network. ```hcl resource "azurerm_resource_group" "test" { - name = "test" + name = "test" location = "West US" } resource "azurerm_virtual_network" "test" { - name = "test" - location = "${azurerm_resource_group.test.location}" + name = "test" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - address_space = ["10.0.0.0/16"] + address_space = ["10.0.0.0/16"] } resource "azurerm_subnet" "test" { - name = "GatewaySubnet" - resource_group_name = "${azurerm_resource_group.test.name}" + name = "GatewaySubnet" + resource_group_name = "${azurerm_resource_group.test.name}" virtual_network_name = "${azurerm_virtual_network.test.name}" - address_prefix = "10.0.1.0/24" + address_prefix = "10.0.1.0/24" } resource "azurerm_local_network_gateway" "onpremise" { - name = "onpremise" - location = "${azurerm_resource_group.test.location}" + name = "onpremise" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - gateway_address = "168.62.225.23" - address_space = ["10.1.1.0/24"] + gateway_address = "168.62.225.23" + address_space = ["10.1.1.0/24"] } resource "azurerm_public_ip" "test" { - name = "test" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" + name = "test" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" public_ip_address_allocation = "Dynamic" } resource "azurerm_virtual_network_gateway" "test" { - name = "test" - location = "${azurerm_resource_group.test.location}" + name = "test" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - type = "Vpn" + type = "Vpn" vpn_type = "RouteBased" active_active = false - enable_bgp = false - sku = "Basic" + enable_bgp = false + sku = "Basic" ip_configuration { - public_ip_address_id = "${azurerm_public_ip.test.id}" + public_ip_address_id = "${azurerm_public_ip.test.id}" private_ip_address_allocation = "Dynamic" - subnet_id = "${azurerm_subnet.test.id}" + subnet_id = "${azurerm_subnet.test.id}" } } resource "azurerm_virtual_network_gateway_connection" "onpremise" { - name = "onpremise" - location = "${azurerm_resource_group.test.location}" + name = "onpremise" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" - type = "IPsec" + type = "IPsec" virtual_network_gateway_id = "${azurerm_virtual_network_gateway.test.id}" - local_network_gateway_id = "${azurerm_local_network_gateway.onpremise.id}" + local_network_gateway_id = "${azurerm_local_network_gateway.onpremise.id}" shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" } @@ -91,108 +91,108 @@ in different locations/regions. ```hcl resource "azurerm_resource_group" "us" { - name = "us" + name = "us" location = "East US" } resource "azurerm_virtual_network" "us" { - name = "us" - location = "${azurerm_resource_group.us.location}" + name = "us" + location = "${azurerm_resource_group.us.location}" resource_group_name = "${azurerm_resource_group.us.name}" - address_space = ["10.0.0.0/16"] + address_space = ["10.0.0.0/16"] } resource "azurerm_subnet" "us_gateway" { - name = "GatewaySubnet" - resource_group_name = "${azurerm_resource_group.us.name}" + name = "GatewaySubnet" + resource_group_name = "${azurerm_resource_group.us.name}" virtual_network_name = "${azurerm_virtual_network.us.name}" - address_prefix = "10.0.1.0/24" + address_prefix = "10.0.1.0/24" } resource "azurerm_public_ip" "us" { - name = "us" - location = "${azurerm_resource_group.us.location}" - resource_group_name = "${azurerm_resource_group.us.name}" + name = "us" + location = "${azurerm_resource_group.us.location}" + resource_group_name = "${azurerm_resource_group.us.name}" public_ip_address_allocation = "Dynamic" } resource "azurerm_virtual_network_gateway" "us" { - name = "us-gateway" - location = "${azurerm_resource_group.us.location}" + name = "us-gateway" + location = "${azurerm_resource_group.us.location}" resource_group_name = "${azurerm_resource_group.us.name}" - type = "Vpn" + type = "Vpn" vpn_type = "RouteBased" - sku = "Basic" + sku = "Basic" ip_configuration { - public_ip_address_id = "${azurerm_public_ip.us.id}" + public_ip_address_id = "${azurerm_public_ip.us.id}" private_ip_address_allocation = "Dynamic" - subnet_id = "${azurerm_subnet.us_gateway.id}" + subnet_id = "${azurerm_subnet.us_gateway.id}" } } resource "azurerm_resource_group" "europe" { - name = "europe" + name = "europe" location = "West Europe" } resource "azurerm_virtual_network" "europe" { - name = "europe" - location = "${azurerm_resource_group.europe.location}" + name = "europe" + location = "${azurerm_resource_group.europe.location}" resource_group_name = "${azurerm_resource_group.europe.name}" - address_space = ["10.1.0.0/16"] + address_space = ["10.1.0.0/16"] } resource "azurerm_subnet" "europe_gateway" { - name = "GatewaySubnet" - resource_group_name = "${azurerm_resource_group.europe.name}" + name = "GatewaySubnet" + resource_group_name = "${azurerm_resource_group.europe.name}" virtual_network_name = "${azurerm_virtual_network.europe.name}" - address_prefix = "10.1.1.0/24" + address_prefix = "10.1.1.0/24" } resource "azurerm_public_ip" "europe" { - name = "europe" - location = "${azurerm_resource_group.europe.location}" - resource_group_name = "${azurerm_resource_group.europe.name}" + name = "europe" + location = "${azurerm_resource_group.europe.location}" + resource_group_name = "${azurerm_resource_group.europe.name}" public_ip_address_allocation = "Dynamic" } resource "azurerm_virtual_network_gateway" "europe" { - name = "europe-gateway" - location = "${azurerm_resource_group.europe.location}" + name = "europe-gateway" + location = "${azurerm_resource_group.europe.location}" resource_group_name = "${azurerm_resource_group.europe.name}" - type = "Vpn" + type = "Vpn" vpn_type = "RouteBased" - sku = "Basic" + sku = "Basic" ip_configuration { - public_ip_address_id = "${azurerm_public_ip.europe.id}" + public_ip_address_id = "${azurerm_public_ip.europe.id}" private_ip_address_allocation = "Dynamic" - subnet_id = "${azurerm_subnet.europe_gateway.id}" + subnet_id = "${azurerm_subnet.europe_gateway.id}" } } resource "azurerm_virtual_network_gateway_connection" "us_to_europe" { - name = "us-to-europe" - location = "${azurerm_resource_group.us.location}" + name = "us-to-europe" + location = "${azurerm_resource_group.us.location}" resource_group_name = "${azurerm_resource_group.us.name}" - type = "Vnet2Vnet" - virtual_network_gateway_id = "${azurerm_virtual_network_gateway.us.id}" + type = "Vnet2Vnet" + virtual_network_gateway_id = "${azurerm_virtual_network_gateway.us.id}" peer_virtual_network_gateway_id = "${azurerm_virtual_network_gateway.europe.id}" shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y" } resource "azurerm_virtual_network_gateway_connection" "europe_to_us" { - name = "europe-to-us" - location = "${azurerm_resource_group.europe.location}" + name = "europe-to-us" + location = "${azurerm_resource_group.europe.location}" resource_group_name = "${azurerm_resource_group.europe.name}" - type = "Vnet2Vnet" - virtual_network_gateway_id = "${azurerm_virtual_network_gateway.europe.id}" + type = "Vnet2Vnet" + virtual_network_gateway_id = "${azurerm_virtual_network_gateway.europe.id}" peer_virtual_network_gateway_id = "${azurerm_virtual_network_gateway.us.id}" shared_key = "4-v3ry-53cr37-1p53c-5h4r3d-k3y"